Merge pull request #57217 from akien-mga/3.4-cherrypicks

This commit is contained in:
Rémi Verschelde 2022-01-25 19:47:43 +01:00 committed by GitHub
commit 4a83366185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 321 additions and 97 deletions

View File

@ -2988,6 +2988,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_GENERIC);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_SRGB);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_LAYERED);
}
void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) {

View File

@ -11,17 +11,6 @@
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<methods>
<method name="get_mix_mode" qualifiers="const">
<return type="int" enum="AnimationNodeOneShot.MixMode" />
<description>
</description>
</method>
<method name="set_mix_mode">
<return type="void" />
<argument index="0" name="mode" type="int" enum="AnimationNodeOneShot.MixMode" />
<description>
</description>
</method>
</methods>
<members>
<member name="autorestart" type="bool" setter="set_autorestart" getter="has_autorestart" default="false">
@ -37,6 +26,8 @@
</member>
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.1">
</member>
<member name="mix_mode" type="int" setter="set_mix_mode" getter="get_mix_mode" enum="AnimationNodeOneShot.MixMode" default="0">
</member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync" default="false">
</member>
</members>

View File

@ -45,7 +45,7 @@
When enabled, an octree containing the scene's lighting information will be computed. This octree will then be used to light dynamic objects in the scene.
</member>
<member name="capture_propagation" type="float" setter="set_capture_propagation" getter="get_capture_propagation" default="1.0">
Bias value to reduce the amount of light proagation in the captured octree.
Bias value to reduce the amount of light propagation in the captured octree.
</member>
<member name="capture_quality" type="int" setter="set_capture_quality" getter="get_capture_quality" enum="BakedLightmap.BakeQuality" default="1">
Bake quality of the capture data.

View File

@ -182,7 +182,7 @@
If [code]true[/code], the [CollisionObject] will continue to receive input events as the mouse is dragged across its shapes.
</member>
<member name="input_ray_pickable" type="bool" setter="set_ray_pickable" getter="is_ray_pickable" default="true">
If [code]true[/code], the [CollisionObject]'s shapes will respond to [RayCast]s.
If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [member collision_layer] bit to be set.
</member>
</members>
<signals>

View File

@ -207,7 +207,7 @@
[b]Note:[/b] A contact is detected if object A is in any of the layers that object B scans, or object B is in any layers that object A scans. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="input_pickable" type="bool" setter="set_pickable" getter="is_pickable" default="true">
If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [code]collision_layer[/code] bit to be set.
If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [member collision_layer] bit to be set.
</member>
</members>
<signals>

View File

@ -9,7 +9,11 @@
- Glow
- Tonemap (Auto Exposure)
- Adjustments
These effects will only apply when the [Viewport]'s intended usage is "3D" or "3D Without Effects". This can be configured for the root Viewport with [member ProjectSettings.rendering/quality/intended_usage/framebuffer_allocation], or for specific Viewports via the [member Viewport.usage] property.
If the target [Viewport] is set to "2D Without Sampling", all post-processing effects will be unavailable. With "3D Without Effects", the following options will be unavailable:
- Ssao
- Ss Reflections
This can be configured for the root Viewport with [member ProjectSettings.rendering/quality/intended_usage/framebuffer_allocation], or for specific Viewports via the [member Viewport.usage] property.
Note that [member ProjectSettings.rendering/quality/intended_usage/framebuffer_allocation] has a mobile platform override to use "3D Without Effects" by default. It improves the performance on mobile devices, but at the same time affects the screen display on mobile devices.
</description>
<tutorials>
<link title="Environment and post-processing">https://docs.godotengine.org/en/3.4/tutorials/3d/environment_and_post_processing.html</link>

View File

@ -603,5 +603,8 @@
<constant name="COMPRESS_SOURCE_NORMAL" value="2" enum="CompressSource">
Source texture (before compression) is a normal texture (e.g. it can be compressed into two channels).
</constant>
<constant name="COMPRESS_SOURCE_LAYERED" value="3" enum="CompressSource">
Source texture (before compression) is a [TextureLayered].
</constant>
</constants>
</class>

View File

@ -24,7 +24,7 @@
<description>
Converts a [Variant] var to JSON text and returns the result. Useful for serializing data to store or send over the network.
[b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a Variant to JSON text will convert all numerical values to [float] types.
Use [code]indent[/code] parameter to pretty print the output.
The [code]indent[/code] parameter controls if and how something is indented, the string used for this parameter will be used where there should be an indent in the output, even spaces like [code]" "[/code] will work. [code]\t[/code] and [code]\n[/code] can also be used for a tab indent, or to make a newline for each indent respectively.
[b]Example output:[/b]
[codeblock]
## JSON.print(my_dictionary)
@ -32,18 +32,34 @@
## JSON.print(my_dictionary, "\t")
{
"name": "my_dictionary",
"version": "1.0.0",
"entities": [
{
"name": "entity_0",
"value": "value_0"
},
{
"name": "entity_1",
"value": "value_1"
}
]
"name": "my_dictionary",
"version": "1.0.0",
"entities": [
{
"name": "entity_0",
"value": "value_0"
},
{
"name": "entity_1",
"value": "value_1"
}
]
}
## JSON.print(my_dictionary, "...")
{
..."name": "my_dictionary",
..."version": "1.0.0",
..."entities": [
......{
........."name": "entity_0",
........."value": "value_0"
......},
......{
........."name": "entity_1",
........."value": "value_1"
......}
...]
}
[/codeblock]
</description>

View File

@ -106,6 +106,9 @@
OS.execute("CMD.exe", ["/C", "cd %TEMP% &amp;&amp; dir"], true, output)
[/codeblock]
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
[b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [code]path[/code], [code]/c[/code] as the first argument, and the desired command as the second argument.
[b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [code]path[/code], [code]-Command[/code] as the first argument, and the desired command as the second argument.
[b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [code]path[/code], [code]-c[/code] as the first argument, and the desired command as the second argument.
</description>
</method>
<method name="find_scancode_from_string" qualifiers="const">

View File

@ -243,7 +243,7 @@
</member>
<member name="bbcode_text" type="String" setter="set_bbcode" getter="get_bbcode" default="&quot;&quot;">
The label's text in BBCode format. Is not representative of manual modifications to the internal tag stack. Erases changes made by other methods when edited.
[b]Note:[/b] It is unadvised to use the [code]+=[/code] operator with [code]bbcode_text[/code] (e.g. [code]bbcode_text += "some string"[/code]) as it replaces the whole text and can cause slowdowns. Use [method append_bbcode] for adding text instead, unless you absolutely need to close a tag that was opened in an earlier method call.
[b]Note:[/b] It is unadvised to use the [code]+=[/code] operator with [code]bbcode_text[/code] (e.g. [code]bbcode_text += "some string"[/code]) as it replaces the whole text and can cause slowdowns. It will also erase all BBCode that was added to stack using [code]push_*[/code] methods. Use [method append_bbcode] for adding text instead, unless you absolutely need to close a tag that was opened in an earlier method call.
</member>
<member name="custom_effects" type="Array" setter="set_effects" getter="get_effects" default="[ ]">
The currently installed custom effects. This is an array of [RichTextEffect]s.

View File

@ -5,6 +5,7 @@
</brief_description>
<description>
A deformable physics body. Used to create elastic or deformable objects such as cloth, rubber, or other flexible materials.
[b]Note:[/b] There are many known bugs in [SoftBody]. Therefore, it's not recommended to use them for things that can affect gameplay (such as a player character made entirely out of soft bodies).
</description>
<tutorials>
<link>https://docs.godotengine.org/en/3.4/tutorials/physics/soft_body.html</link>

View File

@ -57,13 +57,15 @@
If [code]true[/code], texture is flipped vertically.
</member>
<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color( 1, 1, 1, 1 )">
A color value that gets multiplied on, could be used for mood-coloring or to simulate the color of light.
A color value used to [i]multiply[/i] the texture's colors. Can be used for mood-coloring or to simulate the color of light.
[b]Note:[/b] If a [member GeometryInstance.material_override] is defined on the [SpriteBase3D], the material override must be configured to take vertex colors into account for albedo. Otherwise, the color defined in [member modulate] will be ignored. For a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] must be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/color] must be inserted in the shader's [code]fragment()[/code] function.
</member>
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2( 0, 0 )">
The texture's drawing offset.
</member>
<member name="opacity" type="float" setter="set_opacity" getter="get_opacity" default="1.0">
The objects' visibility on a scale from [code]0[/code] fully invisible to [code]1[/code] fully visible.
The texture's visibility on a scale from [code]0[/code] (fully invisible) to [code]1[/code] (fully visible). [member opacity] is a multiplier for the [member modulate] color's alpha channel.
[b]Note:[/b] If a [member GeometryInstance.material_override] is defined on the [SpriteBase3D], the material override must be configured to take vertex colors into account for albedo. Otherwise, the opacity defined in [member opacity] will be ignored. For a [SpatialMaterial], [member SpatialMaterial.vertex_color_use_as_albedo] must be [code]true[/code]. For a [ShaderMaterial], [code]ALPHA *= COLOR.a;[/color] must be inserted in the shader's [code]fragment()[/code] function.
</member>
<member name="pixel_size" type="float" setter="set_pixel_size" getter="get_pixel_size" default="0.01">
The size of one pixel's width on the sprite to scale it in 3D.

View File

@ -750,13 +750,24 @@
<method name="to_float">
<return type="float" />
<description>
Converts a string containing a decimal number into a [code]float[/code].
Converts a string containing a decimal number into a [code]float[/code]. The method will stop on the first non-number character except the first [code].[/code] (decimal point), and [code]e[/code] which is used for exponential.
[codeblock]
print("12.3".to_float()) # 12.3
print("1.2.3".to_float()) # 1.2
print("12ab3".to_float()) # 12
print("1e3".to_float()) # 1000
[/codeblock]
</description>
</method>
<method name="to_int">
<return type="int" />
<description>
Converts a string containing an integer number into an [code]int[/code].
Converts a string containing an integer number into an [code]int[/code]. The method will remove any non-number character and stop if it encounters a [code].[/code].
[codeblock]
print("123".to_int()) # 123
print("a1b2c3".to_int()) # 123
print("1.2.3".to_int()) # 1
[/codeblock]
</description>
</method>
<method name="to_lower">

View File

@ -427,6 +427,7 @@
<argument index="1" name="color" type="Color" />
<description>
Sets the tile's modulation color.
[b]Note:[/b] Modulation is performed by setting the tile's vertex color. To access this in a shader, use [code]COLOR[/code] rather than [code]MODULATE[/code] (which instead accesses the [TileMap]'s [member CanvasItem.modulate] property).
</description>
</method>
<method name="tile_set_name">

View File

@ -38,7 +38,7 @@
<argument index="0" name="parent" type="Object" default="null" />
<argument index="1" name="idx" type="int" default="-1" />
<description>
Creates an item in the tree and adds it as a child of [code]parent[/code].
Creates an item in the tree and adds it as a child of [code]parent[/code], which can be either a valid [TreeItem] or [code]null[/code].
If [code]parent[/code] is [code]null[/code], the root item will be the parent, or the new item will be the root itself if the tree is empty.
The new item will be the [code]idx[/code]th child of parent, or it will be the last child if there are not enough siblings.
</description>
@ -117,7 +117,7 @@
<argument index="0" name="item" type="Object" />
<argument index="1" name="column" type="int" default="-1" />
<description>
Returns the rectangle area for the specified item. If [code]column[/code] is specified, only get the position and size of that column, otherwise get the rectangle containing all columns.
Returns the rectangle area for the specified [TreeItem]. If [code]column[/code] is specified, only get the position and size of that column, otherwise get the rectangle containing all columns.
</description>
</method>
<method name="get_item_at_position" qualifiers="const">
@ -131,7 +131,7 @@
<return type="TreeItem" />
<argument index="0" name="from" type="Object" />
<description>
Returns the next selected item after the given one, or [code]null[/code] if the end is reached.
Returns the next selected [TreeItem] after the given one, or [code]null[/code] if the end is reached.
If [code]from[/code] is [code]null[/code], this returns the first selected item.
</description>
</method>
@ -173,7 +173,7 @@
<return type="void" />
<argument index="0" name="item" type="Object" />
<description>
Causes the [Tree] to jump to the specified item.
Causes the [Tree] to jump to the specified [TreeItem].
</description>
</method>
<method name="set_column_expand">

View File

@ -321,7 +321,7 @@ def main(): # type: () -> None
for path in args.path:
# Cut off trailing slashes so os.path.basename doesn't choke.
if path.endswith(os.sep):
if path.endswith("/") or path.endswith("\\"):
path = path[:-1]
if os.path.basename(path) == "modules":

View File

@ -77,7 +77,6 @@ void ResourceFormatDummyTexture::get_recognized_extensions(List<String> *p_exten
p_extensions->push_back("png");
p_extensions->push_back("pvr");
p_extensions->push_back("svg");
p_extensions->push_back("svgz");
p_extensions->push_back("tga");
p_extensions->push_back("webp");
}
@ -99,7 +98,6 @@ String ResourceFormatDummyTexture::get_resource_type(const String &p_path) const
extension == "png" ||
extension == "pvr" ||
extension == "svg" ||
extension == "svgz" ||
extension == "tga" ||
extension == "webp") {
return "ImageTexture";

View File

@ -204,7 +204,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, float roughness, in
float t = mix(1.0, max(NdotL, NdotV), step(0.0, s));
float sigma2 = roughness * roughness; // TODO: this needs checking
vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse_color / (sigma2 + 0.13));
vec3 A = 1.0 + sigma2 * (-0.5 / (sigma2 + 0.33) + 0.17 * diffuse / (sigma2 + 0.13));
float B = 0.45 * sigma2 / (sigma2 + 0.09);
diffuse_brdf_NL = cNdotL * (A + vec3(B) * s / t) * (1.0 / M_PI);

View File

@ -186,8 +186,7 @@ void main() {
}
vec2 final_pos;
float grad;
grad = steps_taken / float(num_steps);
float grad = (steps_taken + 1.0) / float(num_steps);
float initial_fade = curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), curve_fade_in);
float fade = pow(clamp(1.0 - grad, 0.0, 1.0), distance_fade) * initial_fade;
final_pos = pos;

View File

@ -140,7 +140,6 @@ void EditorAssetInstaller::open(const String &p_path, int p_depth) {
extension_guess["jpeg"] = tree->get_icon("ImageTexture", "EditorIcons");
extension_guess["png"] = tree->get_icon("ImageTexture", "EditorIcons");
extension_guess["svg"] = tree->get_icon("ImageTexture", "EditorIcons");
extension_guess["svgz"] = tree->get_icon("ImageTexture", "EditorIcons");
extension_guess["tga"] = tree->get_icon("ImageTexture", "EditorIcons");
extension_guess["webp"] = tree->get_icon("ImageTexture", "EditorIcons");

View File

@ -1498,6 +1498,10 @@ void EditorFileSystem::update_file(const String &p_file) {
_queue_update_script_classes();
}
Set<String> EditorFileSystem::get_valid_extensions() const {
return valid_extensions;
}
Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector<String> &p_files) {
String importer_name;

View File

@ -262,6 +262,7 @@ public:
void scan_changes();
void get_changed_sources(List<String> *r_changed);
void update_file(const String &p_file);
Set<String> get_valid_extensions() const;
EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
String get_file_type(const String &p_file) const;

View File

@ -1436,12 +1436,18 @@ void FileSystemDock::_folder_removed(String p_folder) {
void FileSystemDock::_rename_operation_confirm() {
String new_name = rename_dialog_text->get_text().strip_edges();
String old_name = tree->get_selected()->get_text(0);
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
} else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
} else if (to_rename.is_file && old_name.get_extension() != new_name.get_extension()) {
if (!EditorFileSystem::get_singleton()->get_valid_extensions().find(new_name.get_extension())) {
EditorNode::get_singleton()->show_warning(TTR("This file extension is not recognized by the editor.\nIf you want to rename it anyway, use your operating system's file manager.\nAfter renaming to an unknown extension, the file won't be shown in the editor anymore."));
return;
}
}
String old_path = to_rename.path.ends_with("/") ? to_rename.path.substr(0, to_rename.path.length() - 1) : to_rename.path;

View File

@ -103,7 +103,7 @@ void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_propert
}
void AnimationNodeBlendTreeEditor::_update_graph() {
if (updating) {
if (updating || blend_tree.is_null()) {
return;
}
@ -915,6 +915,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
}
void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) {
if (le == nullptr) {
return; // The text_submitted signal triggered the graph update and freed the LineEdit.
}
_node_renamed(le->call("get_text"), p_node);
}

View File

@ -136,9 +136,11 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
continue;
}
//Transform shape_transform = sb->shape_owner_get_transform(E->get());
//shape_transform.set_origin(shape_transform.get_origin() - phys_offset);
Transform shape_transform;
if (p_apply_xforms) {
shape_transform = mi->get_transform();
}
shape_transform *= sb->get_transform() * sb->shape_owner_get_transform(E->get());
for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) {
Ref<Shape> collision = sb->shape_owner_get_shape(E->get(), k);
@ -147,7 +149,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
}
MeshLibrary::ShapeData shape_data;
shape_data.shape = collision;
shape_data.local_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get());
shape_data.local_transform = shape_transform;
collisions.push_back(shape_data);
}
}

View File

@ -258,7 +258,7 @@ void ProjectSettingsEditor::_device_input_add() {
int idx = edit_idx;
Dictionary old_val = ProjectSettings::get_singleton()->get(name);
Dictionary action = old_val.duplicate();
Array events = action["events"];
Array events = action["events"].duplicate();
switch (add_type) {
case INPUT_MOUSE_BUTTON: {
@ -383,7 +383,7 @@ void ProjectSettingsEditor::_press_a_key_confirm() {
Dictionary old_val = ProjectSettings::get_singleton()->get(name);
Dictionary action = old_val.duplicate();
Array events = action["events"];
Array events = action["events"].duplicate();
for (int i = 0; i < events.size(); i++) {
Ref<InputEventKey> aie = events[i];
@ -654,7 +654,7 @@ void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column,
Dictionary action = old_val.duplicate();
int idx = ti->get_metadata(0);
Array events = action["events"];
Array events = action["events"].duplicate();
ERR_FAIL_INDEX(idx, events.size());
events.remove(idx);
action["events"] = events;

View File

@ -284,6 +284,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und
vbc->add_child(lbl_preview_title);
lbl_preview = memnew(Label);
lbl_preview->set_autowrap(true);
vbc->add_child(lbl_preview);
// ---- Dialog related

View File

@ -1413,7 +1413,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
GLOBAL_DEF("application/config/icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon",
PropertyInfo(Variant::STRING, "application/config/icon",
PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"));
PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"));
GLOBAL_DEF("application/config/macos_native_icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/macos_native_icon", PropertyInfo(Variant::STRING, "application/config/macos_native_icon", PROPERTY_HINT_FILE, "*.icns"));

View File

@ -1757,7 +1757,7 @@ CSGBrush *CSGPolygon::_build_brush() {
}
int shape_sides = shape_polygon.size();
Vector<int> shape_faces = Geometry::triangulate_polygon(shape_polygon);
ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon");
ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon. Make sure the polygon doesn't have any intersecting edges.");
// Get polygon enclosing Rect2.
Rect2 shape_rect(shape_polygon[0], Vector2());

View File

@ -4,7 +4,7 @@
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon] for drawing 2D extruded polygons to be used as CSG nodes.
</description>
<tutorials>
</tutorials>

View File

@ -4,7 +4,7 @@
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
An array of 2D points is extruded to quickly and easily create a variety of 3D meshes.
An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh] for using 3D meshes as CSG nodes.
</description>
<tutorials>
</tutorials>
@ -48,7 +48,8 @@
When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling.
</member>
<member name="polygon" type="PoolVector2Array" setter="set_polygon" getter="get_polygon" default="PoolVector2Array( 0, 0, 0, 1, 1, 1, 1, 0 )">
The point array that defines the 2D polygon that is extruded.
The point array that defines the 2D polygon that is extruded. This can be a convex or concave polygon with 3 or more points. The polygon must [i]not[/i] have any intersecting edges. Otherwise, triangulation will fail and no mesh will be generated.
[b]Note:[/b] If only 1 or 2 points are defined in [member polygon], no mesh will be generated.
</member>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false">
If [code]true[/code], applies smooth shading to the extrusions.

View File

@ -7,8 +7,8 @@
A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [ARVRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on.
</description>
<tutorials>
<link>https://docs.godotengine.org/en/3.4/tutorials/scripting/gdnative/gdnative-c-example.html</link>
<link>https://docs.godotengine.org/en/3.4/tutorials/scripting/gdnative/gdnative-cpp-example.html</link>
<link>https://docs.godotengine.org/en/3.4/tutorials/scripting/gdnative/gdnative_c_example.html</link>
<link>https://docs.godotengine.org/en/3.4/tutorials/scripting/gdnative/gdnative_cpp_example.html</link>
</tutorials>
<methods>
<method name="get_current_dependencies" qualifiers="const">

View File

@ -473,7 +473,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
err_text = "Left operand of 'is' was already freed.";
OPCODE_BREAK;
}
if (b->is_invalid_object()) {
if (b->get_type() != Variant::OBJECT || b->is_invalid_object()) {
err_text = "Right operand of 'is' is not a class.";
OPCODE_BREAK;
}

View File

@ -318,6 +318,9 @@ namespace Godot.Collections
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_KeyValuePairAt_Generic(IntPtr ptr, int index, out object key, out object value, int valueTypeEncoding, IntPtr valueTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
@ -484,7 +487,7 @@ namespace Godot.Collections
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value);
Dictionary.godot_icall_Dictionary_KeyValuePairAt_Generic(GetPtr(), index, out object key, out object value, valTypeEncoding, valTypeClass);
return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
}

View File

@ -240,6 +240,12 @@ void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObjec
*value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index));
}
void godot_icall_Dictionary_KeyValuePairAt_Generic(Dictionary *ptr, int index, MonoObject **key, MonoObject **value, uint32_t value_type_encoding, GDMonoClass *value_type_class) {
ManagedType type(value_type_encoding, value_type_class);
*key = GDMonoMarshal::variant_to_mono_object(ptr->get_key_at_index(index));
*value = GDMonoMarshal::variant_to_mono_object(ptr->get_value_at_index(index), type);
}
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
@ -350,6 +356,7 @@ void godot_register_collections_icalls() {
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt_Generic", godot_icall_Dictionary_KeyValuePairAt_Generic);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);

View File

@ -132,14 +132,31 @@ void gd_mono_debug_init() {
CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
if (da_args.length()) {
// Clear to avoid passing it to child processes
OS::get_singleton()->set_environment("GODOT_MONO_DEBUGGER_AGENT", String());
} else {
// Try with command line arguments. This is useful on platforms where it's difficult to pass
// environment variables. The command line arguments can be specified in the export options.
String da_cmdline_arg;
List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
const String &arg = E->get();
if (arg.begins_with("--mono-debugger-agent=")) {
da_cmdline_arg = arg;
break;
}
}
if (da_cmdline_arg.length()) {
da_cmdline_arg.replace_first("--mono-debugger-agent=", "--debugger-agent=");
da_args = da_cmdline_arg.utf8();
}
}
#ifdef TOOLS_ENABLED
int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
if (Engine::get_singleton()->is_editor_hint() ||
ProjectSettings::get_singleton()->get_resource_path().empty() ||
Main::is_project_manager()) {
@ -148,6 +165,12 @@ void gd_mono_debug_init() {
}
if (da_args.length() == 0) {
// Use project settings defaults for the editor player
int da_port = GLOBAL_DEF("mono/debugger_agent/port", 23685);
bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
.utf8();

View File

@ -246,6 +246,8 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end)
if (res < 0) {
pcre2_match_data_free_16(match);
pcre2_match_context_free_16(mctx);
return nullptr;
}

View File

@ -152,7 +152,6 @@ Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("svg");
p_extensions->push_back("svgz");
}
ImageLoaderSVG::ImageLoaderSVG() {

View File

@ -28,6 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "export.h"
#include "core/io/image_loader.h"
#include "core/io/json.h"
#include "core/io/stream_peer_ssl.h"
@ -658,9 +660,9 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal Ui,Browser"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color()));
}

View File

@ -28,6 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "export.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "editor/editor_export.h"

View File

@ -3822,7 +3822,7 @@ Error OS_X11::move_to_trash(const String &p_path) {
// Create needed directories for decided trash can location.
{
DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error err = dir_access->make_dir_recursive(trash_path);
// Issue an error if trash can is not created proprely.
@ -3831,7 +3831,6 @@ Error OS_X11::move_to_trash(const String &p_path) {
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/files");
err = dir_access->make_dir_recursive(trash_path + "/info");
ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\"/info");
memdelete(dir_access);
}
// The trash can is successfully created, now we check that we don't exceed our file name length limit.
@ -3871,16 +3870,15 @@ Error OS_X11::move_to_trash(const String &p_path) {
String trash_info = "[Trash Info]\nPath=" + p_path.http_escape() + "\nDeletionDate=" + timestamp + "\n";
{
Error err;
FileAccess *file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
FileAccessRef file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file:" + trash_path + "/info/" + file_name + ".trashinfo");
file->store_string(trash_info);
file->close();
// Rename our resource before moving it to the trash can.
DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = dir_access->rename(p_path, p_path.get_base_dir() + "/" + file_name);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + p_path + "\"");
memdelete(dir_access);
}
// Move the given resource to the trash can.

View File

@ -1278,7 +1278,7 @@ void CPUParticles2D::_bind_methods() {
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "emission_rect_extents"), "set_emission_rect_extents", "get_emission_rect_extents");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
@ -1314,7 +1314,7 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL);
ADD_GROUP("Damping", "");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING);
ADD_GROUP("Angle", "");

View File

@ -389,7 +389,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound>
GeometryInstance *gi = Object::cast_to<GeometryInstance>(p_at_node);
if (gi) {
all_override = mi->get_material_override();
all_override = gi->get_material_override();
}
for (int i = 0; i < bmeshes.size(); i += 2) {
@ -414,8 +414,8 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound>
mf.mesh = mesh;
if (gi) {
mf.cast_shadows = mi->get_cast_shadows_setting() != GeometryInstance::SHADOW_CASTING_SETTING_OFF;
mf.generate_lightmap = mi->get_generate_lightmap();
mf.cast_shadows = gi->get_cast_shadows_setting() != GeometryInstance::SHADOW_CASTING_SETTING_OFF;
mf.generate_lightmap = gi->get_generate_lightmap();
} else {
mf.cast_shadows = true;
mf.generate_lightmap = true;

View File

@ -1410,7 +1410,7 @@ void CPUParticles::_bind_methods() {
ADD_GROUP("Emission Shape", "emission_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points, Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals");
@ -1454,7 +1454,7 @@ void CPUParticles::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL);
ADD_GROUP("Damping", "");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_param", "get_param", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING);
ADD_GROUP("Angle", "");

View File

@ -319,6 +319,8 @@ void AnimationNodeOneShot::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync);
ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync);
ADD_PROPERTY(PropertyInfo(Variant::INT, "mix_mode", PROPERTY_HINT_ENUM, "Blend,Add"), "set_mix_mode", "get_mix_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fadein_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadein_time", "get_fadein_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fadeout_time", PROPERTY_HINT_RANGE, "0,60,0.01,or_greater"), "set_fadeout_time", "get_fadeout_time");

View File

@ -684,8 +684,8 @@ bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const {
void LineEdit::_notification(int p_what) {
switch (p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
@ -694,8 +694,15 @@ void LineEdit::_notification(int p_what) {
EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");
}
}
} break;
#endif
update_cached_width();
update_placeholder_width();
} break;
case NOTIFICATION_THEME_CHANGED: {
update_cached_width();
update_placeholder_width();
update();
} break;
case NOTIFICATION_RESIZED: {
scroll_offset = 0;
set_cursor_position(get_cursor_position());

View File

@ -387,7 +387,6 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
}
void TextureProgress::_notification(int p_what) {
const float corners[12] = { -0.125, -0.375, -0.625, -0.875, 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875 };
switch (p_what) {
case NOTIFICATION_DRAW: {
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
@ -452,7 +451,7 @@ void TextureProgress::_notification(int p_what) {
float val = get_as_ratio() * rad_max_degrees / 360;
if (val == 1) {
Rect2 region = Rect2(progress_offset, s);
Rect2 source = Rect2(Point2(), s);
Rect2 source = Rect2(Point2(), progress->get_size());
draw_texture_rect_region(progress, region, source, tint_progress);
} else if (val != 0) {
Array pts;
@ -466,16 +465,14 @@ void TextureProgress::_notification(int p_what) {
}
float end = start + direction * val;
pts.append(start);
pts.append(end);
float from = MIN(start, end);
float to = MAX(start, end);
for (int i = 0; i < 12; i++) {
if (corners[i] > from && corners[i] < to) {
pts.append(corners[i]);
}
pts.append(from);
for (float corner = Math::floor(from * 4 + 0.5) * 0.25 + 0.125; corner < to; corner += 0.25) {
pts.append(corner);
}
pts.sort();
pts.append(to);
Vector<Point2> uvs;
Vector<Point2> points;
uvs.push_back(get_relative_center());
@ -492,6 +489,8 @@ void TextureProgress::_notification(int p_what) {
colors.push_back(tint_progress);
draw_polygon(points, colors, uvs, progress);
}
// Draw a reference cross.
if (Engine::get_singleton()->is_editor_hint()) {
Point2 p;

View File

@ -1928,6 +1928,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
#endif
node = res->instance(ges);
ERR_FAIL_COND_V(!node, nullptr);
node->set_scene_instance_load_placeholder(get_scene_instance_load_placeholder());
instanced = true;

View File

@ -30,6 +30,8 @@
#include "mesh_library.h"
#include "box_shape.h"
bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
if (name.begins_with("item/")) {
@ -254,12 +256,35 @@ int MeshLibrary::get_last_unused_item_id() const {
}
void MeshLibrary::_set_item_shapes(int p_item, const Array &p_shapes) {
ERR_FAIL_COND(p_shapes.size() & 1);
Array arr_shapes = p_shapes;
int size = p_shapes.size();
if (size & 1) {
ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
int prev_size = item_map[p_item].shapes.size() * 2;
if (prev_size < size) {
// Check if last element is a shape.
Ref<Shape> shape = arr_shapes[size - 1];
if (shape.is_null()) {
Ref<BoxShape> box_shape;
box_shape.instance();
arr_shapes[size - 1] = box_shape;
}
// Make sure the added element is a Transform.
arr_shapes.push_back(Transform());
size++;
} else {
size--;
arr_shapes.resize(size);
}
}
Vector<ShapeData> shapes;
for (int i = 0; i < p_shapes.size(); i += 2) {
for (int i = 0; i < size; i += 2) {
ShapeData sd;
sd.shape = p_shapes[i + 0];
sd.local_transform = p_shapes[i + 1];
sd.shape = arr_shapes[i + 0];
sd.local_transform = arr_shapes[i + 1];
if (sd.shape.is_valid()) {
shapes.push_back(sd);

View File

@ -74,6 +74,95 @@ bool PortalGameplayMonitor::_source_rooms_changed(const int *p_source_room_ids,
return source_rooms_changed;
}
void PortalGameplayMonitor::unload(PortalRenderer &p_portal_renderer) {
// First : send gameplay exit signals for any objects still in gameplay
////////////////////////////////////////////////////////////////////
// lock output
VisualServerCallbacks *callbacks = VSG::scene->get_callbacks();
callbacks->lock();
// Remove any movings
for (int n = 0; n < _active_moving_pool_ids_prev->size(); n++) {
int pool_id = (*_active_moving_pool_ids_prev)[n];
PortalRenderer::Moving &moving = p_portal_renderer.get_pool_moving(pool_id);
moving.last_gameplay_tick_hit = 0;
VisualServerCallbacks::Message msg;
msg.object_id = VSG::scene->_instance_get_object_ID(moving.instance);
msg.type = _exit_callback_type;
callbacks->push_message(msg);
}
// Remove any roaming ghosts
for (int n = 0; n < _active_rghost_pool_ids_prev->size(); n++) {
int pool_id = (*_active_rghost_pool_ids_prev)[n];
PortalRenderer::RGhost &moving = p_portal_renderer.get_pool_rghost(pool_id);
moving.last_gameplay_tick_hit = 0;
VisualServerCallbacks::Message msg;
msg.object_id = moving.object_id;
msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
callbacks->push_message(msg);
}
// Rooms
for (int n = 0; n < _active_room_ids_prev->size(); n++) {
int room_id = (*_active_room_ids_prev)[n];
VSRoom &room = p_portal_renderer.get_room(room_id);
room.last_gameplay_tick_hit = 0;
VisualServerCallbacks::Message msg;
msg.object_id = room._godot_instance_ID;
msg.type = _exit_callback_type;
callbacks->push_message(msg);
}
// RoomGroups
for (int n = 0; n < _active_roomgroup_ids_prev->size(); n++) {
int roomgroup_id = (*_active_roomgroup_ids_prev)[n];
VSRoomGroup &roomgroup = p_portal_renderer.get_roomgroup(roomgroup_id);
roomgroup.last_gameplay_tick_hit = 0;
VisualServerCallbacks::Message msg;
msg.object_id = roomgroup._godot_instance_ID;
msg.type = _exit_callback_type;
callbacks->push_message(msg);
}
// Static Ghosts
for (int n = 0; n < _active_sghost_ids_prev->size(); n++) {
int id = (*_active_sghost_ids_prev)[n];
VSStaticGhost &ghost = p_portal_renderer.get_static_ghost(id);
ghost.last_gameplay_tick_hit = 0;
VisualServerCallbacks::Message msg;
msg.object_id = ghost.object_id;
msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
callbacks->push_message(msg);
}
// unlock
callbacks->unlock();
// Clear all remaining data
for (int n = 0; n < 2; n++) {
_active_moving_pool_ids[n].clear();
_active_rghost_pool_ids[n].clear();
_active_room_ids[n].clear();
_active_roomgroup_ids[n].clear();
_active_sghost_ids[n].clear();
}
_source_rooms_prev.clear();
// Lets not reset this just in case because it may be possible to have a moving outside the room system
// which is preserved between levels, and has a stored gameplay tick. And with uint32_t this should take
// a *long* time to rollover... (828 days?). And I don't think a rollover would actually cause a problem in practice.
// But can revisit this in the case of e.g. servers running continuously.
// We could alternatively go through all movings (not just active) etc and reset the last_gameplay_tick_hit to 0.
// _gameplay_tick = 1;
}
void PortalGameplayMonitor::set_params(bool p_use_secondary_pvs, bool p_use_signals) {
_use_secondary_pvs = p_use_secondary_pvs;
_use_signals = p_use_signals;

View File

@ -43,6 +43,8 @@ class PortalGameplayMonitor {
public:
PortalGameplayMonitor();
void unload(PortalRenderer &p_portal_renderer);
// entering and exiting gameplay notifications (requires PVS)
void update_gameplay(PortalRenderer &p_portal_renderer, const int *p_source_room_ids, int p_num_source_rooms);
void set_params(bool p_use_secondary_pvs, bool p_use_signals);

View File

@ -996,6 +996,7 @@ void PortalRenderer::sprawl_roaming(uint32_t p_mover_pool_id, MovingBase &r_movi
void PortalRenderer::_ensure_unloaded(String p_reason) {
if (_loaded) {
_loaded = false;
_gameplay_monitor.unload(*this);
String str;
if (p_reason != String()) {
@ -1014,6 +1015,17 @@ void PortalRenderer::_ensure_unloaded(String p_reason) {
void PortalRenderer::rooms_and_portals_clear() {
_loaded = false;
// N.B. We want to make sure all the tick counters on movings rooms etc to zero,
// so that on loading the next level gameplay entered signals etc will be
// correctly sent and everything is fresh.
// This is mostly done by the gameplay_monitor, but rooms_and_portals_clear()
// will also clear tick counters where possible
// (there is no TrackedList for the RoomGroup pool for example).
// This could be made neater by moving everything to TrackedPooledLists, but this
// may be overkill.
_gameplay_monitor.unload(*this);
_statics.clear();
_static_ghosts.clear();

View File

@ -87,6 +87,9 @@ public:
void destroy() {
_rooms.clear();
room_id = -1;
last_tick_hit = 0;
last_gameplay_tick_hit = 0;
}
// the expanded aabb allows objects to move on most frames

View File

@ -257,6 +257,7 @@ struct VSRoom {
_secondary_pvs_size = 0;
_priority = 0;
_contains_internal_rooms = false;
last_gameplay_tick_hit = 0;
}
void cleanup_after_conversion() {

View File

@ -1141,7 +1141,7 @@ void VisualServerCanvas::canvas_light_occluder_set_polygon(RID p_occluder, RID p
ERR_FAIL_COND(!occluder);
if (occluder->polygon.is_valid()) {
LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.get(p_polygon);
LightOccluderPolygon *occluder_poly = canvas_light_occluder_polygon_owner.get(occluder->polygon);
if (occluder_poly) {
occluder_poly->owners.erase(occluder);
}