feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ Point2 AnimatedSprite2D::_edit_get_pivot() const {
|
|||
bool AnimatedSprite2D::_edit_use_pivot() const {
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 AnimatedSprite2D::_edit_get_rect() const {
|
||||
return _get_rect();
|
||||
}
|
||||
|
|
@ -75,7 +77,7 @@ bool AnimatedSprite2D::_edit_use_rect() const {
|
|||
}
|
||||
return t.is_valid();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Rect2 AnimatedSprite2D::get_anchorable_rect() const {
|
||||
return _get_rect();
|
||||
|
|
@ -111,7 +113,7 @@ Rect2 AnimatedSprite2D::_get_rect() const {
|
|||
}
|
||||
|
||||
void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (!frames.is_valid()) {
|
||||
if (frames.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +168,7 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const {
|
|||
void AnimatedSprite2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !frames.is_null() && frames->has_animation(autoplay)) {
|
||||
if (!Engine::get_singleton()->is_editor_hint() && frames.is_valid() && frames->has_animation(autoplay)) {
|
||||
play(autoplay);
|
||||
}
|
||||
} break;
|
||||
|
|
@ -463,7 +465,7 @@ void AnimatedSprite2D::play(const StringName &p_name, float p_custom_scale, bool
|
|||
name = animation;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_MSG(frames, vformat("There is no animation with name '%s'.", name));
|
||||
ERR_FAIL_COND_MSG(frames.is_null(), vformat("There is no animation with name '%s'.", name));
|
||||
ERR_FAIL_COND_MSG(!frames->get_animation_names().has(name), vformat("There is no animation with name '%s'.", name));
|
||||
|
||||
if (frames->get_frame_count(name) == 0) {
|
||||
|
|
@ -541,7 +543,7 @@ void AnimatedSprite2D::set_animation(const StringName &p_name) {
|
|||
|
||||
emit_signal(SceneStringName(animation_changed));
|
||||
|
||||
if (frames == nullptr) {
|
||||
if (frames.is_null()) {
|
||||
animation = StringName();
|
||||
stop();
|
||||
ERR_FAIL_MSG(vformat("There is no animation with name '%s'.", p_name));
|
||||
|
|
@ -593,7 +595,7 @@ void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_
|
|||
}
|
||||
Node2D::get_argument_options(p_function, p_idx, r_options);
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
bool AnimatedSprite2D::_set(const StringName &p_name, const Variant &p_value) {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class AnimatedSprite2D : public Node2D {
|
|||
String autoplay;
|
||||
|
||||
bool playing = false;
|
||||
StringName animation = "default";
|
||||
StringName animation = SceneStringName(default_);
|
||||
int frame = 0;
|
||||
float speed_scale = 1.0;
|
||||
float custom_speed_scale = 1.0;
|
||||
|
|
@ -66,7 +66,7 @@ class AnimatedSprite2D : public Node2D {
|
|||
protected:
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
|
@ -79,9 +79,12 @@ public:
|
|||
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
|
||||
virtual Point2 _edit_get_pivot() const override;
|
||||
virtual bool _edit_use_pivot() const override;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
virtual Rect2 get_anchorable_rect() const override;
|
||||
|
||||
|
|
@ -129,7 +132,7 @@ public:
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
AnimatedSprite2D();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "audio_listener_2d.h"
|
||||
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
bool AudioListener2D::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (p_name == "current") {
|
||||
if (p_value.operator bool()) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define AUDIO_LISTENER_2D_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
class AudioListener2D : public Node2D {
|
||||
GDCLASS(AudioListener2D, Node2D);
|
||||
|
|
|
|||
|
|
@ -210,6 +210,14 @@ float AudioStreamPlayer2D::get_volume_db() const {
|
|||
return internal->volume_db;
|
||||
}
|
||||
|
||||
void AudioStreamPlayer2D::set_volume_linear(float p_volume) {
|
||||
set_volume_db(Math::linear_to_db(p_volume));
|
||||
}
|
||||
|
||||
float AudioStreamPlayer2D::get_volume_linear() const {
|
||||
return Math::db_to_linear(get_volume_db());
|
||||
}
|
||||
|
||||
void AudioStreamPlayer2D::set_pitch_scale(float p_pitch_scale) {
|
||||
internal->set_pitch_scale(p_pitch_scale);
|
||||
}
|
||||
|
|
@ -242,7 +250,7 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
|
|||
|
||||
void AudioStreamPlayer2D::stop() {
|
||||
setplay.set(-1);
|
||||
internal->stop();
|
||||
internal->stop_basic();
|
||||
}
|
||||
|
||||
bool AudioStreamPlayer2D::is_playing() const {
|
||||
|
|
@ -253,6 +261,9 @@ bool AudioStreamPlayer2D::is_playing() const {
|
|||
}
|
||||
|
||||
float AudioStreamPlayer2D::get_playback_position() {
|
||||
if (setplay.get() >= 0) {
|
||||
return setplay.get(); // play() has been called this frame, but no playback exists just yet.
|
||||
}
|
||||
return internal->get_playback_position();
|
||||
}
|
||||
|
||||
|
|
@ -276,10 +287,6 @@ void AudioStreamPlayer2D::_set_playing(bool p_enable) {
|
|||
internal->set_playing(p_enable);
|
||||
}
|
||||
|
||||
bool AudioStreamPlayer2D::_is_active() const {
|
||||
return internal->is_active();
|
||||
}
|
||||
|
||||
void AudioStreamPlayer2D::_validate_property(PropertyInfo &p_property) const {
|
||||
internal->validate_property(p_property);
|
||||
}
|
||||
|
|
@ -369,6 +376,9 @@ void AudioStreamPlayer2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer2D::set_volume_db);
|
||||
ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer2D::get_volume_db);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_volume_linear", "volume_linear"), &AudioStreamPlayer2D::set_volume_linear);
|
||||
ClassDB::bind_method(D_METHOD("get_volume_linear"), &AudioStreamPlayer2D::get_volume_linear);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_pitch_scale", "pitch_scale"), &AudioStreamPlayer2D::set_pitch_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_pitch_scale"), &AudioStreamPlayer2D::get_pitch_scale);
|
||||
|
||||
|
|
@ -385,8 +395,7 @@ void AudioStreamPlayer2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer2D::set_autoplay);
|
||||
ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer2D::is_autoplay_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer2D::_set_playing);
|
||||
ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer2D::_is_active);
|
||||
ClassDB::bind_method(D_METHOD("set_playing", "enable"), &AudioStreamPlayer2D::_set_playing);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_distance", "pixels"), &AudioStreamPlayer2D::set_max_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_max_distance"), &AudioStreamPlayer2D::get_max_distance);
|
||||
|
|
@ -414,8 +423,9 @@ void AudioStreamPlayer2D::_bind_methods() {
|
|||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,suffix:dB"), "set_volume_db", "get_volume_db");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_linear", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_volume_linear", "get_volume_linear");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_ONESHOT, "", PROPERTY_USAGE_EDITOR), "set_playing", "is_playing");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "1,4096,1,or_greater,exp,suffix:px"), "set_max_distance", "get_max_distance");
|
||||
|
|
@ -430,7 +440,7 @@ void AudioStreamPlayer2D::_bind_methods() {
|
|||
}
|
||||
|
||||
AudioStreamPlayer2D::AudioStreamPlayer2D() {
|
||||
internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true));
|
||||
internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), callable_mp(this, &AudioStreamPlayer2D::stop), true));
|
||||
cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength");
|
||||
set_hide_clip_children(true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ public:
|
|||
void set_volume_db(float p_volume);
|
||||
float get_volume_db() const;
|
||||
|
||||
void set_volume_linear(float p_volume);
|
||||
float get_volume_linear() const;
|
||||
|
||||
void set_pitch_scale(float p_pitch_scale);
|
||||
float get_pitch_scale() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ void BackBufferCopy::_update_copy_mode() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 BackBufferCopy::_edit_get_rect() const {
|
||||
return rect;
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ Rect2 BackBufferCopy::_edit_get_rect() const {
|
|||
bool BackBufferCopy::_edit_use_rect() const {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Rect2 BackBufferCopy::get_anchorable_rect() const {
|
||||
return rect;
|
||||
|
|
|
|||
|
|
@ -54,10 +54,10 @@ protected:
|
|||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_rect(const Rect2 &p_rect);
|
||||
Rect2 get_rect() const;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "camera_2d.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
bool Camera2D::_is_editing_in_editor() const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -115,11 +115,11 @@ void Camera2D::set_zoom(const Vector2 &p_zoom) {
|
|||
Point2 old_smoothed_camera_pos = smoothed_camera_pos;
|
||||
_update_scroll();
|
||||
smoothed_camera_pos = old_smoothed_camera_pos;
|
||||
};
|
||||
}
|
||||
|
||||
Vector2 Camera2D::get_zoom() const {
|
||||
return zoom;
|
||||
};
|
||||
}
|
||||
|
||||
Transform2D Camera2D::get_camera_transform() {
|
||||
if (!get_tree()) {
|
||||
|
|
@ -277,7 +277,7 @@ void Camera2D::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
case NOTIFICATION_READY: {
|
||||
if (Engine::get_singleton()->is_editor_hint() && is_part_of_edited_scene()) {
|
||||
if (is_part_of_edited_scene()) {
|
||||
ProjectSettings::get_singleton()->connect(SNAME("settings_changed"), callable_mp(this, &Camera2D::_project_settings_changed));
|
||||
}
|
||||
} break;
|
||||
|
|
@ -300,8 +300,10 @@ void Camera2D::_notification(int p_what) {
|
|||
// Force the limits etc. to update.
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
_interpolation_data.xform_prev = _interpolation_data.xform_curr;
|
||||
_update_process_callback();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_PAUSED: {
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
_update_scroll();
|
||||
|
|
@ -314,7 +316,9 @@ void Camera2D::_notification(int p_what) {
|
|||
}
|
||||
if (is_physics_interpolated_and_enabled()) {
|
||||
_ensure_update_interpolation_data();
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
if (Engine::get_singleton()->is_in_physics_frame()) {
|
||||
_interpolation_data.xform_curr = get_camera_transform();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
@ -387,7 +391,7 @@ void Camera2D::_notification(int p_what) {
|
|||
inv_camera_transform.xform(Vector2(0, screen_size.height))
|
||||
};
|
||||
|
||||
Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space
|
||||
Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width);
|
||||
|
|
@ -401,13 +405,13 @@ void Camera2D::_notification(int p_what) {
|
|||
limit_drawing_width = 3;
|
||||
}
|
||||
|
||||
Vector2 camera_origin = get_global_position();
|
||||
Vector2 camera_scale = get_global_scale().abs();
|
||||
Transform2D inv_transform = get_global_transform().affine_inverse();
|
||||
|
||||
Vector2 limit_points[4] = {
|
||||
(Vector2(limit[SIDE_LEFT], limit[SIDE_TOP]) - camera_origin) / camera_scale,
|
||||
(Vector2(limit[SIDE_RIGHT], limit[SIDE_TOP]) - camera_origin) / camera_scale,
|
||||
(Vector2(limit[SIDE_RIGHT], limit[SIDE_BOTTOM]) - camera_origin) / camera_scale,
|
||||
(Vector2(limit[SIDE_LEFT], limit[SIDE_BOTTOM]) - camera_origin) / camera_scale
|
||||
inv_transform.xform(Vector2(limit[SIDE_LEFT], limit[SIDE_TOP])),
|
||||
inv_transform.xform(Vector2(limit[SIDE_RIGHT], limit[SIDE_TOP])),
|
||||
inv_transform.xform(Vector2(limit[SIDE_RIGHT], limit[SIDE_BOTTOM])),
|
||||
inv_transform.xform(Vector2(limit[SIDE_LEFT], limit[SIDE_BOTTOM]))
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
|
@ -432,7 +436,7 @@ void Camera2D::_notification(int p_what) {
|
|||
inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[SIDE_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[SIDE_BOTTOM])))
|
||||
};
|
||||
|
||||
Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space
|
||||
Transform2D inv_transform = get_global_transform().affine_inverse(); // Undo global space.
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width);
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ Color CanvasModulate::get_color() const {
|
|||
}
|
||||
|
||||
PackedStringArray CanvasModulate::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (is_in_canvas && is_visible_in_tree()) {
|
||||
List<Node *> nodes;
|
||||
|
|
|
|||
41
engine/scene/2d/cpu_particles_2d.compat.inc
Normal file
41
engine/scene/2d/cpu_particles_2d.compat.inc
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**************************************************************************/
|
||||
/* cpu_particles_2d.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void CPUParticles2D::_restart_bind_compat_92089() {
|
||||
restart(false);
|
||||
}
|
||||
|
||||
void CPUParticles2D::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("restart"), &CPUParticles2D::_restart_bind_compat_92089);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
|
@ -29,9 +29,13 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "cpu_particles_2d.h"
|
||||
#include "cpu_particles_2d.compat.inc"
|
||||
|
||||
#include "core/math/random_number_generator.h"
|
||||
#include "core/math/transform_interpolator.h"
|
||||
#include "scene/2d/gpu_particles_2d.h"
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
#include "scene/resources/canvas_item_material.h"
|
||||
#include "scene/resources/curve_texture.h"
|
||||
#include "scene/resources/gradient_texture.h"
|
||||
#include "scene/resources/particle_process_material.h"
|
||||
|
|
@ -41,10 +45,22 @@ void CPUParticles2D::set_emitting(bool p_emitting) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (p_emitting && !use_fixed_seed) {
|
||||
set_seed(Math::rand());
|
||||
}
|
||||
|
||||
emitting = p_emitting;
|
||||
if (emitting) {
|
||||
active = true;
|
||||
set_process_internal(true);
|
||||
_set_emitting();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUParticles2D::_set_emitting() {
|
||||
active = true;
|
||||
set_process_internal(true);
|
||||
// first update before rendering to avoid one frame delay after emitting starts
|
||||
if (time == 0) {
|
||||
_update_internal();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +109,14 @@ void CPUParticles2D::set_lifetime_randomness(double p_random) {
|
|||
|
||||
void CPUParticles2D::set_use_local_coordinates(bool p_enable) {
|
||||
local_coords = p_enable;
|
||||
set_notify_transform(!p_enable);
|
||||
|
||||
// Prevent sending item transforms when using global coords,
|
||||
// and inform the RenderingServer to use identity mode.
|
||||
set_canvas_item_use_identity_transform(!local_coords);
|
||||
|
||||
// We only need NOTIFICATION_TRANSFORM_CHANGED
|
||||
// when following an interpolated target.
|
||||
set_notify_transform(_interpolation_data.interpolated_follow);
|
||||
}
|
||||
|
||||
void CPUParticles2D::set_speed_scale(double p_scale) {
|
||||
|
|
@ -164,10 +187,10 @@ void CPUParticles2D::_update_mesh_texture() {
|
|||
};
|
||||
|
||||
Vector<Vector2> uvs;
|
||||
AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture);
|
||||
if (atlas_texure && atlas_texure->get_atlas().is_valid()) {
|
||||
Rect2 region_rect = atlas_texure->get_region();
|
||||
Size2 atlas_size = atlas_texure->get_atlas()->get_size();
|
||||
AtlasTexture *atlas_texture = Object::cast_to<AtlasTexture>(*texture);
|
||||
if (atlas_texture && atlas_texture->get_atlas().is_valid()) {
|
||||
Rect2 region_rect = atlas_texture->get_region();
|
||||
Size2 atlas_size = atlas_texture->get_atlas()->get_size();
|
||||
uvs.push_back(Vector2(region_rect.position.x / atlas_size.x, region_rect.position.y / atlas_size.y));
|
||||
uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, region_rect.position.y / atlas_size.y));
|
||||
uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, (region_rect.position.y + region_rect.size.y) / atlas_size.y));
|
||||
|
|
@ -225,6 +248,27 @@ void CPUParticles2D::_texture_changed() {
|
|||
}
|
||||
}
|
||||
|
||||
void CPUParticles2D::_refresh_interpolation_state() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The logic for whether to do an interpolated follow.
|
||||
// This is rather complex, but basically:
|
||||
// If project setting interpolation is ON and this particle system is in global mode,
|
||||
// we will follow the INTERPOLATED position rather than the actual position.
|
||||
// This is so that particles aren't generated AHEAD of the interpolated parent.
|
||||
bool follow = !local_coords && get_tree()->is_physics_interpolation_enabled();
|
||||
|
||||
if (follow == _interpolation_data.interpolated_follow) {
|
||||
return;
|
||||
}
|
||||
|
||||
_interpolation_data.interpolated_follow = follow;
|
||||
|
||||
set_physics_process_internal(_interpolation_data.interpolated_follow);
|
||||
}
|
||||
|
||||
Ref<Texture2D> CPUParticles2D::get_texture() const {
|
||||
return texture;
|
||||
}
|
||||
|
|
@ -260,7 +304,7 @@ PackedStringArray CPUParticles2D::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
void CPUParticles2D::restart() {
|
||||
void CPUParticles2D::restart(bool p_keep_seed) {
|
||||
time = 0;
|
||||
frame_remainder = 0;
|
||||
cycle = 0;
|
||||
|
|
@ -274,8 +318,12 @@ void CPUParticles2D::restart() {
|
|||
w[i].active = false;
|
||||
}
|
||||
}
|
||||
if (!p_keep_seed && !use_fixed_seed) {
|
||||
seed = Math::rand();
|
||||
}
|
||||
|
||||
set_emitting(true);
|
||||
emitting = true;
|
||||
_set_emitting();
|
||||
}
|
||||
|
||||
void CPUParticles2D::set_direction(Vector2 p_direction) {
|
||||
|
|
@ -328,7 +376,7 @@ real_t CPUParticles2D::get_param_max(Parameter p_param) const {
|
|||
|
||||
static void _adjust_curve_range(const Ref<Curve> &p_curve, real_t p_min, real_t p_max) {
|
||||
Ref<Curve> curve = p_curve;
|
||||
if (!curve.is_valid()) {
|
||||
if (curve.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +553,35 @@ bool CPUParticles2D::get_split_scale() {
|
|||
return split_scale;
|
||||
}
|
||||
|
||||
void CPUParticles2D::set_use_fixed_seed(bool p_use_fixed_seed) {
|
||||
if (p_use_fixed_seed == use_fixed_seed) {
|
||||
return;
|
||||
}
|
||||
use_fixed_seed = p_use_fixed_seed;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool CPUParticles2D::get_use_fixed_seed() const {
|
||||
return use_fixed_seed;
|
||||
}
|
||||
|
||||
void CPUParticles2D::set_seed(uint32_t p_seed) {
|
||||
seed = p_seed;
|
||||
}
|
||||
|
||||
uint32_t CPUParticles2D::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void CPUParticles2D::request_particles_process(real_t p_requested_process_time) {
|
||||
_requested_process_time = p_requested_process_time;
|
||||
}
|
||||
|
||||
void CPUParticles2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "emitting") {
|
||||
p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE;
|
||||
}
|
||||
|
||||
if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
|
@ -532,6 +608,10 @@ void CPUParticles2D::_validate_property(PropertyInfo &p_property) const {
|
|||
if (p_property.name.begins_with("scale_curve_") && !split_scale) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
|
||||
if (p_property.name == "seed" && !use_fixed_seed) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t idhash(uint32_t x) {
|
||||
|
|
@ -562,6 +642,9 @@ void CPUParticles2D::_update_internal() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Change update mode?
|
||||
_refresh_interpolation_state();
|
||||
|
||||
double delta = get_process_delta_time();
|
||||
if (!active && !emitting) {
|
||||
set_process_internal(false);
|
||||
|
|
@ -574,25 +657,28 @@ void CPUParticles2D::_update_internal() {
|
|||
return;
|
||||
}
|
||||
_set_do_redraw(true);
|
||||
|
||||
if (time == 0 && pre_process_time > 0.0) {
|
||||
double frame_time;
|
||||
if (fixed_fps > 0) {
|
||||
frame_time = 1.0 / fixed_fps;
|
||||
} else {
|
||||
frame_time = 1.0 / 30.0;
|
||||
}
|
||||
|
||||
double todo = pre_process_time;
|
||||
|
||||
while (todo >= 0) {
|
||||
_particles_process(frame_time);
|
||||
todo -= frame_time;
|
||||
}
|
||||
double frame_time;
|
||||
if (fixed_fps > 0) {
|
||||
frame_time = 1.0 / fixed_fps;
|
||||
} else {
|
||||
frame_time = 1.0 / 30.0;
|
||||
}
|
||||
double todo = _requested_process_time;
|
||||
_requested_process_time = 0;
|
||||
if (time == 0 && pre_process_time > 0.0) {
|
||||
todo += pre_process_time;
|
||||
}
|
||||
real_t tmp_speed = speed_scale;
|
||||
speed_scale = 1.0;
|
||||
while (todo > 0) {
|
||||
_particles_process(frame_time);
|
||||
todo -= frame_time;
|
||||
}
|
||||
speed_scale = tmp_speed;
|
||||
|
||||
todo = 0.0;
|
||||
|
||||
if (fixed_fps > 0) {
|
||||
double frame_time = 1.0 / fixed_fps;
|
||||
double decr = frame_time;
|
||||
|
||||
double ldelta = delta;
|
||||
|
|
@ -601,13 +687,12 @@ void CPUParticles2D::_update_internal() {
|
|||
} else if (ldelta <= 0.0) { //unlikely but..
|
||||
ldelta = 0.001;
|
||||
}
|
||||
double todo = frame_remainder + ldelta;
|
||||
todo = frame_remainder + ldelta;
|
||||
|
||||
while (todo >= frame_time) {
|
||||
_particles_process(frame_time);
|
||||
todo -= decr;
|
||||
}
|
||||
|
||||
frame_remainder = todo;
|
||||
|
||||
} else {
|
||||
|
|
@ -639,7 +724,11 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
Transform2D emission_xform;
|
||||
Transform2D velocity_xform;
|
||||
if (!local_coords) {
|
||||
emission_xform = get_global_transform();
|
||||
if (!_interpolation_data.interpolated_follow) {
|
||||
emission_xform = get_global_transform();
|
||||
} else {
|
||||
TransformInterpolator::interpolate_transform_2d(_interpolation_data.global_xform_prev, _interpolation_data.global_xform_curr, emission_xform, Engine::get_singleton()->get_physics_interpolation_fraction());
|
||||
}
|
||||
velocity_xform = emission_xform;
|
||||
velocity_xform[2] = Vector2();
|
||||
}
|
||||
|
|
@ -662,13 +751,13 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
double restart_phase = double(i) / double(pcount);
|
||||
|
||||
if (randomness_ratio > 0.0) {
|
||||
uint32_t seed = cycle;
|
||||
uint32_t _seed = cycle;
|
||||
if (restart_phase >= system_phase) {
|
||||
seed -= uint32_t(1);
|
||||
_seed -= uint32_t(1);
|
||||
}
|
||||
seed *= uint32_t(pcount);
|
||||
seed += uint32_t(i);
|
||||
double random = double(idhash(seed) % uint32_t(65536)) / 65536.0;
|
||||
_seed *= uint32_t(pcount);
|
||||
_seed += uint32_t(i);
|
||||
double random = double(idhash(_seed) % uint32_t(65536)) / 65536.0;
|
||||
restart_phase += randomness_ratio * random * 1.0 / double(pcount);
|
||||
}
|
||||
|
||||
|
|
@ -729,22 +818,23 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv);
|
||||
}
|
||||
|
||||
p.seed = Math::rand();
|
||||
p.seed = seed + uint32_t(i) + i + cycle;
|
||||
rng->set_seed(p.seed);
|
||||
|
||||
p.angle_rand = Math::randf();
|
||||
p.scale_rand = Math::randf();
|
||||
p.hue_rot_rand = Math::randf();
|
||||
p.anim_offset_rand = Math::randf();
|
||||
p.angle_rand = rng->randf();
|
||||
p.scale_rand = rng->randf();
|
||||
p.hue_rot_rand = rng->randf();
|
||||
p.anim_offset_rand = rng->randf();
|
||||
|
||||
if (color_initial_ramp.is_valid()) {
|
||||
p.start_color_rand = color_initial_ramp->get_color_at_offset(Math::randf());
|
||||
p.start_color_rand = color_initial_ramp->get_color_at_offset(rng->randf());
|
||||
} else {
|
||||
p.start_color_rand = Color(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
real_t angle1_rad = direction.angle() + Math::deg_to_rad((Math::randf() * 2.0 - 1.0) * spread);
|
||||
real_t angle1_rad = direction.angle() + Math::deg_to_rad((rng->randf() * 2.0 - 1.0) * spread);
|
||||
Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad));
|
||||
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], (real_t)Math::randf());
|
||||
p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], rng->randf());
|
||||
|
||||
real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
|
||||
p.rotation = Math::deg_to_rad(base_angle);
|
||||
|
|
@ -752,7 +842,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
p.custom[0] = 0.0; // unused
|
||||
p.custom[1] = 0.0; // phase [0..1]
|
||||
p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand);
|
||||
p.custom[3] = (1.0 - Math::randf() * lifetime_randomness);
|
||||
p.custom[3] = (1.0 - rng->randf() * lifetime_randomness);
|
||||
p.transform = Transform2D();
|
||||
p.time = 0;
|
||||
p.lifetime = lifetime * p.custom[3];
|
||||
|
|
@ -763,17 +853,17 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
//do none
|
||||
} break;
|
||||
case EMISSION_SHAPE_SPHERE: {
|
||||
real_t t = Math_TAU * Math::randf();
|
||||
real_t radius = emission_sphere_radius * Math::randf();
|
||||
real_t t = Math_TAU * rng->randf();
|
||||
real_t radius = emission_sphere_radius * rng->randf();
|
||||
p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
|
||||
} break;
|
||||
case EMISSION_SHAPE_SPHERE_SURFACE: {
|
||||
real_t s = Math::randf(), t = Math_TAU * Math::randf();
|
||||
real_t s = rng->randf(), t = Math_TAU * rng->randf();
|
||||
real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s);
|
||||
p.transform[2] = Vector2(Math::cos(t), Math::sin(t)) * radius;
|
||||
} break;
|
||||
case EMISSION_SHAPE_RECTANGLE: {
|
||||
p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0) * emission_rect_extents;
|
||||
p.transform[2] = Vector2(rng->randf() * 2.0 - 1.0, rng->randf() * 2.0 - 1.0) * emission_rect_extents;
|
||||
} break;
|
||||
case EMISSION_SHAPE_POINTS:
|
||||
case EMISSION_SHAPE_DIRECTED_POINTS: {
|
||||
|
|
@ -814,8 +904,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
p.active = false;
|
||||
tv = 1.0;
|
||||
} else {
|
||||
uint32_t alt_seed = p.seed;
|
||||
|
||||
uint32_t _seed = p.seed;
|
||||
p.time += local_delta;
|
||||
p.custom[1] = p.time / lifetime;
|
||||
tv = p.time / p.lifetime;
|
||||
|
|
@ -873,18 +962,18 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
Vector2 pos = p.transform[2];
|
||||
|
||||
//apply linear acceleration
|
||||
force += p.velocity.length() > 0.0 ? p.velocity.normalized() * tex_linear_accel * Math::lerp(parameters_min[PARAM_LINEAR_ACCEL], parameters_max[PARAM_LINEAR_ACCEL], rand_from_seed(alt_seed)) : Vector2();
|
||||
force += p.velocity.length() > 0.0 ? p.velocity.normalized() * tex_linear_accel * Math::lerp(parameters_min[PARAM_LINEAR_ACCEL], parameters_max[PARAM_LINEAR_ACCEL], rand_from_seed(_seed)) : Vector2();
|
||||
//apply radial acceleration
|
||||
Vector2 org = emission_xform[2];
|
||||
Vector2 diff = pos - org;
|
||||
force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(alt_seed)) : Vector2();
|
||||
force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(_seed)) : Vector2();
|
||||
//apply tangential acceleration;
|
||||
Vector2 yx = Vector2(diff.y, diff.x);
|
||||
force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector2();
|
||||
force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(_seed))) : Vector2();
|
||||
//apply attractor forces
|
||||
p.velocity += force * local_delta;
|
||||
//orbit velocity
|
||||
real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(alt_seed));
|
||||
real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(_seed));
|
||||
if (orbit_amount != 0.0) {
|
||||
real_t ang = orbit_amount * local_delta * Math_TAU;
|
||||
// Not sure why the ParticleProcessMaterial code uses a clockwise rotation matrix,
|
||||
|
|
@ -899,7 +988,7 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
|
||||
if (parameters_max[PARAM_DAMPING] + tex_damping > 0.0) {
|
||||
real_t v = p.velocity.length();
|
||||
real_t damp = tex_damping * Math::lerp(parameters_min[PARAM_DAMPING], parameters_max[PARAM_DAMPING], rand_from_seed(alt_seed));
|
||||
real_t damp = tex_damping * Math::lerp(parameters_min[PARAM_DAMPING], parameters_max[PARAM_DAMPING], rand_from_seed(_seed));
|
||||
v -= damp * local_delta;
|
||||
if (v < 0.0) {
|
||||
p.velocity = Vector2();
|
||||
|
|
@ -908,9 +997,9 @@ void CPUParticles2D::_particles_process(double p_delta) {
|
|||
}
|
||||
}
|
||||
real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand);
|
||||
base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed));
|
||||
base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(_seed));
|
||||
p.rotation = Math::deg_to_rad(base_angle); //angle
|
||||
p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed));
|
||||
p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + tv * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(_seed));
|
||||
}
|
||||
//apply color
|
||||
//apply hue rotation
|
||||
|
|
@ -1102,6 +1191,17 @@ void CPUParticles2D::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
set_process_internal(emitting);
|
||||
|
||||
_refresh_interpolation_state();
|
||||
|
||||
set_physics_process_internal(emitting && _interpolation_data.interpolated_follow);
|
||||
|
||||
// If we are interpolated following, then reset physics interpolation
|
||||
// when first appearing. This won't be called by canvas item, as in the
|
||||
// following mode, is_physics_interpolated() is actually FALSE.
|
||||
if (_interpolation_data.interpolated_follow) {
|
||||
notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
|
|
@ -1130,37 +1230,28 @@ void CPUParticles2D::_notification(int p_what) {
|
|||
_update_internal();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (_interpolation_data.interpolated_follow) {
|
||||
// Keep the interpolated follow target updated.
|
||||
_interpolation_data.global_xform_prev = _interpolation_data.global_xform_curr;
|
||||
_interpolation_data.global_xform_curr = get_global_transform();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
inv_emission_transform = get_global_transform().affine_inverse();
|
||||
|
||||
if (!local_coords) {
|
||||
int pc = particles.size();
|
||||
|
||||
float *w = particle_data.ptrw();
|
||||
const Particle *r = particles.ptr();
|
||||
float *ptr = w;
|
||||
|
||||
for (int i = 0; i < pc; i++) {
|
||||
Transform2D t = inv_emission_transform * r[i].transform;
|
||||
|
||||
if (r[i].active) {
|
||||
ptr[0] = t.columns[0][0];
|
||||
ptr[1] = t.columns[1][0];
|
||||
ptr[2] = 0;
|
||||
ptr[3] = t.columns[2][0];
|
||||
ptr[4] = t.columns[0][1];
|
||||
ptr[5] = t.columns[1][1];
|
||||
ptr[6] = 0;
|
||||
ptr[7] = t.columns[2][1];
|
||||
|
||||
} else {
|
||||
memset(ptr, 0, sizeof(float) * 8);
|
||||
}
|
||||
|
||||
ptr += 16;
|
||||
if (_interpolation_data.interpolated_follow) {
|
||||
// If the transform has been updated AFTER the physics tick, keep data flowing.
|
||||
if (Engine::get_singleton()->is_in_physics_frame()) {
|
||||
_interpolation_data.global_xform_curr = get_global_transform();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
||||
// Make sure current is up to date with any pending global transform changes.
|
||||
_interpolation_data.global_xform_curr = get_global_transform_const();
|
||||
_interpolation_data.global_xform_prev = _interpolation_data.global_xform_curr;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1262,6 +1353,7 @@ void CPUParticles2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles2D::set_fixed_fps);
|
||||
ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &CPUParticles2D::set_fractional_delta);
|
||||
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &CPUParticles2D::set_speed_scale);
|
||||
ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &CPUParticles2D::request_particles_process);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_emitting"), &CPUParticles2D::is_emitting);
|
||||
ClassDB::bind_method(D_METHOD("get_amount"), &CPUParticles2D::get_amount);
|
||||
|
|
@ -1275,6 +1367,11 @@ void CPUParticles2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles2D::get_fixed_fps);
|
||||
ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles2D::get_fractional_delta);
|
||||
ClassDB::bind_method(D_METHOD("get_speed_scale"), &CPUParticles2D::get_speed_scale);
|
||||
ClassDB::bind_method(D_METHOD("set_use_fixed_seed", "use_fixed_seed"), &CPUParticles2D::set_use_fixed_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_use_fixed_seed"), &CPUParticles2D::get_use_fixed_seed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_seed", "seed"), &CPUParticles2D::set_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_seed"), &CPUParticles2D::get_seed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &CPUParticles2D::set_draw_order);
|
||||
|
||||
|
|
@ -1283,17 +1380,20 @@ void CPUParticles2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &CPUParticles2D::set_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_texture"), &CPUParticles2D::get_texture);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("restart"), &CPUParticles2D::restart);
|
||||
ClassDB::bind_method(D_METHOD("restart", "keep_seed"), &CPUParticles2D::restart, DEFVAL(false));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); // FIXME: Evaluate support for `exp` in integer properties, or remove this.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
|
||||
ADD_GROUP("Time", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,suffix:s"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,suffix:s"), "set_pre_process_time", "get_pre_process_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_fixed_seed"), "set_use_fixed_seed", "get_use_fixed_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed", PROPERTY_HINT_RANGE, "0," + itos(UINT32_MAX) + ",1"), "set_seed", "get_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
|
||||
|
|
@ -1301,11 +1401,12 @@ void CPUParticles2D::_bind_methods() {
|
|||
// No visibility_rect property contrarily to Particles2D, it's updated automatically.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
|
||||
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
|
||||
|
||||
ADD_PROPERTY_DEFAULT("seed", 0);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_direction", "direction"), &CPUParticles2D::set_direction);
|
||||
|
|
@ -1475,6 +1576,9 @@ CPUParticles2D::CPUParticles2D() {
|
|||
set_emitting(true);
|
||||
set_amount(8);
|
||||
set_use_local_coordinates(false);
|
||||
set_seed(Math::rand());
|
||||
|
||||
rng.instantiate();
|
||||
|
||||
set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
|
||||
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
|
||||
|
|
@ -1509,6 +1613,12 @@ CPUParticles2D::CPUParticles2D() {
|
|||
set_color(Color(1, 1, 1, 1));
|
||||
|
||||
_update_mesh_texture();
|
||||
|
||||
// CPUParticles2D defaults to interpolation off.
|
||||
// This is because the result often looks better when the particles are updated every frame.
|
||||
// Note that children will need to explicitly turn back on interpolation if they want to use it,
|
||||
// rather than relying on inherit mode.
|
||||
set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_OFF);
|
||||
}
|
||||
|
||||
CPUParticles2D::~CPUParticles2D() {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class RandomNumberGenerator;
|
||||
|
||||
class CPUParticles2D : public Node2D {
|
||||
private:
|
||||
GDCLASS(CPUParticles2D, Node2D);
|
||||
|
|
@ -133,6 +135,7 @@ private:
|
|||
|
||||
double lifetime = 1.0;
|
||||
double pre_process_time = 0.0;
|
||||
double _requested_process_time = 0.0;
|
||||
real_t explosiveness_ratio = 0.0;
|
||||
real_t randomness_ratio = 0.0;
|
||||
double lifetime_randomness = 0.0;
|
||||
|
|
@ -140,6 +143,8 @@ private:
|
|||
bool local_coords = false;
|
||||
int fixed_fps = 0;
|
||||
bool fractional_delta = true;
|
||||
uint32_t seed = 0;
|
||||
bool use_fixed_seed = false;
|
||||
|
||||
Transform2D inv_emission_transform;
|
||||
|
||||
|
|
@ -176,12 +181,24 @@ private:
|
|||
|
||||
Vector2 gravity = Vector2(0, 980);
|
||||
|
||||
Ref<RandomNumberGenerator> rng;
|
||||
|
||||
void _update_internal();
|
||||
void _particles_process(double p_delta);
|
||||
void _update_particle_data_buffer();
|
||||
void _set_emitting();
|
||||
|
||||
Mutex update_mutex;
|
||||
|
||||
struct InterpolationData {
|
||||
// Whether this particle is non-interpolated, but following an interpolated parent.
|
||||
bool interpolated_follow = false;
|
||||
|
||||
// If doing interpolated follow, we need to keep these updated per tick.
|
||||
Transform2D global_xform_curr;
|
||||
Transform2D global_xform_prev;
|
||||
} _interpolation_data;
|
||||
|
||||
void _update_render_thread();
|
||||
|
||||
void _update_mesh_texture();
|
||||
|
|
@ -190,11 +207,18 @@ private:
|
|||
|
||||
void _texture_changed();
|
||||
|
||||
void _refresh_interpolation_state();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _restart_bind_compat_92089();
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
void set_emitting(bool p_emitting);
|
||||
void set_amount(int p_amount);
|
||||
|
|
@ -230,6 +254,14 @@ public:
|
|||
void set_texture(const Ref<Texture2D> &p_texture);
|
||||
Ref<Texture2D> get_texture() const;
|
||||
|
||||
void set_use_fixed_seed(bool p_use_fixed_seed);
|
||||
bool get_use_fixed_seed() const;
|
||||
|
||||
void set_seed(uint32_t p_seed);
|
||||
uint32_t get_seed() const;
|
||||
|
||||
void request_particles_process(real_t p_requested_process_time);
|
||||
|
||||
///////////////////
|
||||
|
||||
void set_direction(Vector2 p_direction);
|
||||
|
|
@ -284,7 +316,7 @@ public:
|
|||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
void restart();
|
||||
void restart(bool p_keep_seed = false);
|
||||
|
||||
void convert_from_particles(Node *p_particles);
|
||||
|
||||
|
|
|
|||
41
engine/scene/2d/gpu_particles_2d.compat.inc
Normal file
41
engine/scene/2d/gpu_particles_2d.compat.inc
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**************************************************************************/
|
||||
/* gpu_particles_2d.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void GPUParticles2D::_restart_bind_compat_92089() {
|
||||
restart(false);
|
||||
}
|
||||
|
||||
void GPUParticles2D::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("restart"), &GPUParticles2D::_restart_bind_compat_92089);
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
|
@ -29,20 +29,21 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "gpu_particles_2d.h"
|
||||
#include "gpu_particles_2d.compat.inc"
|
||||
|
||||
#include "scene/2d/cpu_particles_2d.h"
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
#include "scene/resources/canvas_item_material.h"
|
||||
#include "scene/resources/curve_texture.h"
|
||||
#include "scene/resources/gradient_texture.h"
|
||||
#include "scene/resources/particle_process_material.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/config/engine.h"
|
||||
#endif
|
||||
|
||||
void GPUParticles2D::set_emitting(bool p_emitting) {
|
||||
// Do not return even if `p_emitting == emitting` because `emitting` is just an approximation.
|
||||
|
||||
if (p_emitting && p_emitting != emitting && !use_fixed_seed) {
|
||||
set_seed(Math::rand());
|
||||
}
|
||||
if (p_emitting && one_shot) {
|
||||
if (!active && !emitting) {
|
||||
// Last cycle ended.
|
||||
|
|
@ -326,6 +327,31 @@ float GPUParticles2D::get_interp_to_end() const {
|
|||
return interp_to_end_factor;
|
||||
}
|
||||
|
||||
void GPUParticles2D::set_use_fixed_seed(bool p_use_fixed_seed) {
|
||||
if (p_use_fixed_seed == use_fixed_seed) {
|
||||
return;
|
||||
}
|
||||
use_fixed_seed = p_use_fixed_seed;
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
||||
bool GPUParticles2D::get_use_fixed_seed() const {
|
||||
return use_fixed_seed;
|
||||
}
|
||||
|
||||
void GPUParticles2D::set_seed(uint32_t p_seed) {
|
||||
seed = p_seed;
|
||||
RS::get_singleton()->particles_set_seed(particles, p_seed);
|
||||
}
|
||||
|
||||
uint32_t GPUParticles2D::get_seed() const {
|
||||
return seed;
|
||||
}
|
||||
|
||||
void GPUParticles2D::request_particles_process(real_t p_requested_process_time) {
|
||||
RS::get_singleton()->particles_request_process_time(particles, p_requested_process_time);
|
||||
}
|
||||
|
||||
PackedStringArray GPUParticles2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
|
|
@ -345,11 +371,11 @@ PackedStringArray GPUParticles2D::get_configuration_warnings() const {
|
|||
}
|
||||
|
||||
if (trail_enabled && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
warnings.push_back(RTR("Particle trails are only available when using the Forward+ or Mobile rendering backends."));
|
||||
warnings.push_back(RTR("Particle trails are only available when using the Forward+ or Mobile renderers."));
|
||||
}
|
||||
|
||||
if (sub_emitter != NodePath() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
warnings.push_back(RTR("Particle sub-emitters are not available when using the GL Compatibility rendering backend."));
|
||||
warnings.push_back(RTR("Particle sub-emitters are not available when using the Compatibility renderer."));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
|
|
@ -384,6 +410,12 @@ Ref<Texture2D> GPUParticles2D::get_texture() const {
|
|||
}
|
||||
|
||||
void GPUParticles2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "seed" && !use_fixed_seed) {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
if (p_property.name == "emitting") {
|
||||
p_property.hint = one_shot ? PROPERTY_HINT_ONESHOT : PROPERTY_HINT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void GPUParticles2D::emit_particle(const Transform2D &p_transform2d, const Vector2 &p_velocity2d, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
|
||||
|
|
@ -440,7 +472,10 @@ float GPUParticles2D::get_amount_ratio() const {
|
|||
return amount_ratio;
|
||||
}
|
||||
|
||||
void GPUParticles2D::restart() {
|
||||
void GPUParticles2D::restart(bool p_keep_seed) {
|
||||
if (!p_keep_seed && !use_fixed_seed) {
|
||||
set_seed(Math::rand());
|
||||
}
|
||||
RS::get_singleton()->particles_restart(particles);
|
||||
RS::get_singleton()->particles_set_emitting(particles, true);
|
||||
|
||||
|
|
@ -643,10 +678,10 @@ void GPUParticles2D::_notification(int p_what) {
|
|||
};
|
||||
|
||||
Vector<Vector2> uvs;
|
||||
AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture);
|
||||
if (atlas_texure && atlas_texure->get_atlas().is_valid()) {
|
||||
Rect2 region_rect = atlas_texure->get_region();
|
||||
Size2 atlas_size = atlas_texure->get_atlas()->get_size();
|
||||
AtlasTexture *atlas_texture = Object::cast_to<AtlasTexture>(*texture);
|
||||
if (atlas_texture && atlas_texture->get_atlas().is_valid()) {
|
||||
Rect2 region_rect = atlas_texture->get_region();
|
||||
Size2 atlas_size = atlas_texture->get_atlas()->get_size();
|
||||
uvs.push_back(Vector2(region_rect.position.x / atlas_size.x, region_rect.position.y / atlas_size.y));
|
||||
uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, region_rect.position.y / atlas_size.y));
|
||||
uvs.push_back(Vector2((region_rect.position.x + region_rect.size.x) / atlas_size.x, (region_rect.position.y + region_rect.size.y) / atlas_size.y));
|
||||
|
|
@ -688,6 +723,7 @@ void GPUParticles2D::_notification(int p_what) {
|
|||
RS::get_singleton()->particles_set_speed_scale(particles, 0);
|
||||
}
|
||||
set_process_internal(true);
|
||||
set_physics_process_internal(true);
|
||||
previous_position = get_global_position();
|
||||
} break;
|
||||
|
||||
|
|
@ -695,6 +731,8 @@ void GPUParticles2D::_notification(int p_what) {
|
|||
RS::get_singleton()->particles_set_subemitter(particles, RID());
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_UNSUSPENDED:
|
||||
case NOTIFICATION_PAUSED:
|
||||
case NOTIFICATION_UNPAUSED: {
|
||||
if (is_inside_tree()) {
|
||||
|
|
@ -711,15 +749,6 @@ void GPUParticles2D::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) /
|
||||
get_process_delta_time();
|
||||
|
||||
if (velocity != previous_velocity) {
|
||||
RS::get_singleton()->particles_set_emitter_velocity(particles, velocity);
|
||||
previous_velocity = velocity;
|
||||
}
|
||||
previous_position = get_global_position();
|
||||
|
||||
if (one_shot) {
|
||||
time += get_process_delta_time();
|
||||
if (time > emission_time) {
|
||||
|
|
@ -739,6 +768,19 @@ void GPUParticles2D::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
// Update velocity in physics process, so that velocity calculations remain correct
|
||||
// if the physics tick rate is lower than the rendered framerate (especially without physics interpolation).
|
||||
const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) /
|
||||
get_physics_process_delta_time();
|
||||
|
||||
if (velocity != previous_velocity) {
|
||||
RS::get_singleton()->particles_set_emitter_velocity(particles, velocity);
|
||||
previous_velocity = velocity;
|
||||
}
|
||||
previous_position = get_global_position();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -760,6 +802,8 @@ void GPUParticles2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles2D::set_collision_base_size);
|
||||
ClassDB::bind_method(D_METHOD("set_interp_to_end", "interp"), &GPUParticles2D::set_interp_to_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("request_particles_process", "process_time"), &GPUParticles2D::request_particles_process);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles2D::is_emitting);
|
||||
ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles2D::get_amount);
|
||||
ClassDB::bind_method(D_METHOD("get_lifetime"), &GPUParticles2D::get_lifetime);
|
||||
|
|
@ -785,7 +829,7 @@ void GPUParticles2D::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("capture_rect"), &GPUParticles2D::capture_rect);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("restart"), &GPUParticles2D::restart);
|
||||
ClassDB::bind_method(D_METHOD("restart", "keep_seed"), &GPUParticles2D::restart, DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_sub_emitter", "path"), &GPUParticles2D::set_sub_emitter);
|
||||
ClassDB::bind_method(D_METHOD("get_sub_emitter"), &GPUParticles2D::get_sub_emitter);
|
||||
|
|
@ -809,26 +853,33 @@ void GPUParticles2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_amount_ratio", "ratio"), &GPUParticles2D::set_amount_ratio);
|
||||
ClassDB::bind_method(D_METHOD("get_amount_ratio"), &GPUParticles2D::get_amount_ratio);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_fixed_seed", "use_fixed_seed"), &GPUParticles2D::set_use_fixed_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_use_fixed_seed"), &GPUParticles2D::get_use_fixed_seed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_seed", "seed"), &GPUParticles2D::set_seed);
|
||||
ClassDB::bind_method(D_METHOD("get_seed"), &GPUParticles2D::get_seed);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("finished"));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting", PROPERTY_HINT_ONESHOT), "set_emitting", "is_emitting");
|
||||
ADD_PROPERTY_DEFAULT("emitting", true); // Workaround for doctool in headless mode, as dummy rasterizer always returns false.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); // FIXME: Evaluate support for `exp` in integer properties, or remove this.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount_ratio", PROPERTY_HINT_RANGE, "0,1,0.0001"), "set_amount_ratio", "get_amount_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles2D"), "set_sub_emitter", "get_sub_emitter");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticleProcessMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
|
||||
ADD_GROUP("Time", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,suffix:s"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp,suffix:s"), "set_lifetime", "get_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.001"), "set_interp_to_end", "get_interp_to_end");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,suffix:s"), "set_pre_process_time", "get_pre_process_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,10.0,0.01,or_greater,exp,suffix:s"), "set_pre_process_time", "get_pre_process_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_fixed_seed", PROPERTY_HINT_ENUM), "set_use_fixed_seed", "get_use_fixed_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed", PROPERTY_HINT_RANGE, "0," + itos(UINT32_MAX) + ",1"), "set_seed", "get_seed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interp_to_end", PROPERTY_HINT_RANGE, "0.00,1.0,0.001"), "set_interp_to_end", "get_interp_to_end");
|
||||
ADD_GROUP("Collision", "collision_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
|
||||
ADD_GROUP("Drawing", "");
|
||||
|
|
@ -840,7 +891,8 @@ void GPUParticles2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_lifetime", PROPERTY_HINT_RANGE, "0.01,10,0.01,or_greater,suffix:s"), "set_trail_lifetime", "get_trail_lifetime");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_sections", PROPERTY_HINT_RANGE, "2,128,1"), "set_trail_sections", "get_trail_sections");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "trail_section_subdivisions", PROPERTY_HINT_RANGE, "1,1024,1"), "set_trail_section_subdivisions", "get_trail_section_subdivisions");
|
||||
|
||||
ADD_GROUP("Process Material", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticleProcessMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX);
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME);
|
||||
BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME);
|
||||
|
|
@ -850,6 +902,8 @@ void GPUParticles2D::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(EMIT_FLAG_VELOCITY);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_COLOR);
|
||||
BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM);
|
||||
|
||||
ADD_PROPERTY_DEFAULT("seed", 0);
|
||||
}
|
||||
|
||||
GPUParticles2D::GPUParticles2D() {
|
||||
|
|
@ -863,6 +917,8 @@ GPUParticles2D::GPUParticles2D() {
|
|||
one_shot = false; // Needed so that set_emitting doesn't access uninitialized values
|
||||
set_emitting(true);
|
||||
set_one_shot(false);
|
||||
set_seed(Math::rand());
|
||||
set_use_fixed_seed(false);
|
||||
set_amount(8);
|
||||
set_amount_ratio(1.0);
|
||||
set_lifetime(1);
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ private:
|
|||
float interp_to_end_factor = 0;
|
||||
Vector3 previous_velocity;
|
||||
Vector2 previous_position;
|
||||
uint32_t seed = 0;
|
||||
bool use_fixed_seed = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool show_visibility_rect = false;
|
||||
#endif
|
||||
|
|
@ -101,6 +103,11 @@ protected:
|
|||
void _notification(int p_what);
|
||||
void _update_collision_size();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _restart_bind_compat_92089();
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
void set_emitting(bool p_emitting);
|
||||
void set_amount(int p_amount);
|
||||
|
|
@ -119,6 +126,7 @@ public:
|
|||
void set_trail_sections(int p_sections);
|
||||
void set_trail_section_subdivisions(int p_subdivisions);
|
||||
void set_interp_to_end(float p_interp);
|
||||
void request_particles_process(real_t p_requested_process_time);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void set_show_visibility_rect(bool p_show_visibility_rect);
|
||||
|
|
@ -166,6 +174,12 @@ public:
|
|||
void set_sub_emitter(const NodePath &p_path);
|
||||
NodePath get_sub_emitter() const;
|
||||
|
||||
void set_use_fixed_seed(bool p_use_fixed_seed);
|
||||
bool get_use_fixed_seed() const;
|
||||
|
||||
void set_seed(uint32_t p_seed);
|
||||
uint32_t get_seed() const;
|
||||
|
||||
enum EmitFlags {
|
||||
EMIT_FLAG_POSITION = RS::PARTICLES_EMIT_FLAG_POSITION,
|
||||
EMIT_FLAG_ROTATION_SCALE = RS::PARTICLES_EMIT_FLAG_ROTATION_SCALE,
|
||||
|
|
@ -176,7 +190,7 @@ public:
|
|||
|
||||
void emit_particle(const Transform2D &p_transform, const Vector2 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
|
||||
|
||||
void restart();
|
||||
void restart(bool p_keep_seed = false);
|
||||
Rect2 capture_rect() const;
|
||||
void convert_from_particles(Node *p_particles);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ void Light2D::_update_light_visibility() {
|
|||
if (editor_only) {
|
||||
editor_ok = false;
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
RS::get_singleton()->canvas_light_set_enabled(canvas_light, enabled && is_visible_in_tree() && editor_ok);
|
||||
}
|
||||
|
|
@ -343,7 +343,6 @@ Light2D::~Light2D() {
|
|||
//////////////////////////////
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
||||
Dictionary PointLight2D::_edit_get_state() const {
|
||||
Dictionary state = Node2D::_edit_get_state();
|
||||
state["offset"] = get_texture_offset();
|
||||
|
|
@ -367,7 +366,9 @@ Point2 PointLight2D::_edit_get_pivot() const {
|
|||
bool PointLight2D::_edit_use_pivot() const {
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 PointLight2D::_edit_get_rect() const {
|
||||
if (texture.is_null()) {
|
||||
return Rect2();
|
||||
|
|
@ -378,9 +379,9 @@ Rect2 PointLight2D::_edit_get_rect() const {
|
|||
}
|
||||
|
||||
bool PointLight2D::_edit_use_rect() const {
|
||||
return !texture.is_null();
|
||||
return texture.is_valid();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Rect2 PointLight2D::get_anchorable_rect() const {
|
||||
if (texture.is_null()) {
|
||||
|
|
@ -394,6 +395,19 @@ Rect2 PointLight2D::get_anchorable_rect() const {
|
|||
void PointLight2D::set_texture(const Ref<Texture2D> &p_texture) {
|
||||
texture = p_texture;
|
||||
if (texture.is_valid()) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (
|
||||
p_texture->is_class("AnimatedTexture") ||
|
||||
p_texture->is_class("AtlasTexture") ||
|
||||
p_texture->is_class("CameraTexture") ||
|
||||
p_texture->is_class("CanvasTexture") ||
|
||||
p_texture->is_class("MeshTexture") ||
|
||||
p_texture->is_class("Texture2DRD") ||
|
||||
p_texture->is_class("ViewportTexture")) {
|
||||
WARN_PRINT(vformat("%s cannot be used as a PointLight2D texture (%s). As a workaround, assign the value returned by %s's `get_image()` instead.", p_texture->get_class(), get_path(), p_texture->get_class()));
|
||||
}
|
||||
#endif
|
||||
|
||||
RS::get_singleton()->canvas_light_set_texture(_get_light(), texture->get_rid());
|
||||
} else {
|
||||
RS::get_singleton()->canvas_light_set_texture(_get_light(), RID());
|
||||
|
|
@ -417,9 +431,9 @@ Vector2 PointLight2D::get_texture_offset() const {
|
|||
}
|
||||
|
||||
PackedStringArray PointLight2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Light2D::get_configuration_warnings();
|
||||
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +475,8 @@ void PointLight2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &PointLight2D::set_texture_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_texture_scale"), &PointLight2D::get_texture_scale);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
|
||||
// Only allow texture types that display correctly.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D,-AnimatedTexture,-AtlasTexture,-CameraTexture,-CanvasTexture,-MeshTexture,-Texture2DRD,-ViewportTexture"), "set_texture", "get_texture");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1024,1,or_greater,suffix:px"), "set_height", "get_height");
|
||||
|
|
|
|||
|
|
@ -160,9 +160,12 @@ public:
|
|||
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
|
||||
virtual Point2 _edit_get_pivot() const override;
|
||||
virtual bool _edit_use_pivot() const override;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
virtual Rect2 get_anchorable_rect() const override;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#define LINE_GRAB_WIDTH 8
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 OccluderPolygon2D::_edit_get_rect() const {
|
||||
if (rect_cache_dirty) {
|
||||
if (closed) {
|
||||
|
|
@ -83,13 +83,14 @@ bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double
|
|||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void OccluderPolygon2D::set_polygon(const Vector<Vector2> &p_polygon) {
|
||||
polygon = p_polygon;
|
||||
rect_cache_dirty = true;
|
||||
RS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed);
|
||||
emit_changed();
|
||||
update_configuration_warning();
|
||||
}
|
||||
|
||||
Vector<Vector2> OccluderPolygon2D::get_polygon() const {
|
||||
|
|
@ -155,7 +156,7 @@ OccluderPolygon2D::~OccluderPolygon2D() {
|
|||
void LightOccluder2D::_poly_changed() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
queue_redraw();
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void LightOccluder2D::_physics_interpolated_changed() {
|
||||
|
|
@ -217,7 +218,7 @@ void LightOccluder2D::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 LightOccluder2D::_edit_get_rect() const {
|
||||
return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2();
|
||||
}
|
||||
|
|
@ -225,14 +226,14 @@ Rect2 LightOccluder2D::_edit_get_rect() const {
|
|||
bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (occluder_polygon.is_valid()) {
|
||||
occluder_polygon->disconnect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
occluder_polygon = p_polygon;
|
||||
|
||||
if (occluder_polygon.is_valid()) {
|
||||
|
|
@ -246,7 +247,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg
|
|||
occluder_polygon->connect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
|
||||
}
|
||||
queue_redraw();
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
Ref<OccluderPolygon2D> LightOccluder2D::get_occluder_polygon() const {
|
||||
|
|
@ -263,9 +264,9 @@ int LightOccluder2D::get_occluder_light_mask() const {
|
|||
}
|
||||
|
||||
PackedStringArray LightOccluder2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (!occluder_polygon.is_valid()) {
|
||||
if (occluder_polygon.is_null()) {
|
||||
warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,11 +56,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
|
||||
#endif
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
void set_polygon(const Vector<Vector2> &p_polygon);
|
||||
Vector<Vector2> get_polygon() const;
|
||||
|
||||
|
|
@ -93,10 +92,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon);
|
||||
Ref<OccluderPolygon2D> get_occluder_polygon() const;
|
||||
|
|
|
|||
|
|
@ -36,18 +36,18 @@
|
|||
Line2D::Line2D() {
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 Line2D::_edit_get_rect() const {
|
||||
if (_points.size() == 0) {
|
||||
return Rect2(0, 0, 0, 0);
|
||||
}
|
||||
Vector2 d = Vector2(_width, _width);
|
||||
Rect2 bounding_rect = Rect2(_points[0] - d, 2 * d);
|
||||
Vector2 min = _points[0];
|
||||
Vector2 max = min;
|
||||
for (int i = 1; i < _points.size(); i++) {
|
||||
bounding_rect.expand_to(_points[i] - d);
|
||||
bounding_rect.expand_to(_points[i] + d);
|
||||
min = min.min(_points[i]);
|
||||
max = max.max(_points[i]);
|
||||
}
|
||||
return bounding_rect;
|
||||
return Rect2(min, max - min).grow(_width);
|
||||
}
|
||||
|
||||
bool Line2D::_edit_use_rect() const {
|
||||
|
|
@ -299,6 +299,9 @@ void Line2D::_draw() {
|
|||
}
|
||||
|
||||
lb.build();
|
||||
if (lb.indices.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RS::get_singleton()->canvas_item_add_triangle_array(
|
||||
get_canvas_item(),
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public:
|
|||
LINE_TEXTURE_STRETCH
|
||||
};
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,11 @@ void LineBuilder::build() {
|
|||
}
|
||||
}
|
||||
|
||||
if (point_count < 2 || (distance_required && Math::is_zero_approx(total_distance))) {
|
||||
// Zero-length line, nothing to build.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_interpolate_color) {
|
||||
color0 = gradient->get_color(0);
|
||||
} else {
|
||||
|
|
@ -370,7 +375,7 @@ void LineBuilder::build() {
|
|||
}
|
||||
|
||||
if (!is_intersecting) {
|
||||
// In this case the joint is too corrupted to be re-used,
|
||||
// In this case the joint is too corrupted to be reused,
|
||||
// start again the strip with fallback points
|
||||
strip_begin(pos_up0, pos_down0, color1, uvx1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void Marker2D::_draw_cross() {
|
|||
draw_multiline_colors(points, colors);
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 Marker2D::_edit_get_rect() const {
|
||||
real_t extents = get_gizmo_extents();
|
||||
return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2));
|
||||
|
|
@ -70,7 +70,7 @@ Rect2 Marker2D::_edit_get_rect() const {
|
|||
bool Marker2D::_edit_use_rect() const {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void Marker2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_gizmo_extents(real_t p_extents);
|
||||
real_t get_gizmo_extents() const;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@
|
|||
|
||||
#include "mesh_instance_2d.h"
|
||||
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
#include "thirdparty/clipper2/include/clipper2/clipper.h"
|
||||
#include "thirdparty/misc/polypartition.h"
|
||||
|
||||
Callable MeshInstance2D::_navmesh_source_geometry_parsing_callback;
|
||||
RID MeshInstance2D::_navmesh_source_geometry_parser;
|
||||
|
||||
void MeshInstance2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
|
|
@ -54,7 +65,23 @@ void MeshInstance2D::_bind_methods() {
|
|||
}
|
||||
|
||||
void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) {
|
||||
if (mesh == p_mesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
mesh->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
|
||||
}
|
||||
|
||||
mesh = p_mesh;
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
// If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback
|
||||
// so do this before connecting to the change signal.
|
||||
mesh->get_rid();
|
||||
mesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +102,7 @@ Ref<Texture2D> MeshInstance2D::get_texture() const {
|
|||
return texture;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 MeshInstance2D::_edit_get_rect() const {
|
||||
if (mesh.is_valid()) {
|
||||
AABB aabb = mesh->get_aabb();
|
||||
|
|
@ -88,7 +115,102 @@ Rect2 MeshInstance2D::_edit_get_rect() const {
|
|||
bool MeshInstance2D::_edit_use_rect() const {
|
||||
return mesh.is_valid();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void MeshInstance2D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&MeshInstance2D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshInstance2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
MeshInstance2D *mesh_instance = Object::cast_to<MeshInstance2D>(p_node);
|
||||
|
||||
if (mesh_instance == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
|
||||
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Mesh> mesh = mesh_instance->get_mesh();
|
||||
if (mesh.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Transform2D mesh_instance_xform = p_source_geometry_data->root_node_transform * mesh_instance->get_global_transform();
|
||||
|
||||
using namespace Clipper2Lib;
|
||||
|
||||
PathsD subject_paths, dummy_clip_paths;
|
||||
|
||||
for (int i = 0; i < mesh->get_surface_count(); i++) {
|
||||
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FLAG_USE_2D_VERTICES)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PathD subject_path;
|
||||
|
||||
int index_count = 0;
|
||||
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
|
||||
index_count = mesh->surface_get_array_index_len(i);
|
||||
} else {
|
||||
index_count = mesh->surface_get_array_len(i);
|
||||
}
|
||||
|
||||
ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0));
|
||||
|
||||
Array a = mesh->surface_get_arrays(i);
|
||||
|
||||
Vector<Vector2> mesh_vertices = a[Mesh::ARRAY_VERTEX];
|
||||
|
||||
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
|
||||
Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX];
|
||||
for (int vertex_index : mesh_indices) {
|
||||
const Vector2 &vertex = mesh_vertices[vertex_index];
|
||||
const PointD &point = PointD(vertex.x, vertex.y);
|
||||
subject_path.push_back(point);
|
||||
}
|
||||
} else {
|
||||
for (const Vector2 &vertex : mesh_vertices) {
|
||||
const PointD &point = PointD(vertex.x, vertex.y);
|
||||
subject_path.push_back(point);
|
||||
}
|
||||
}
|
||||
subject_paths.push_back(subject_path);
|
||||
}
|
||||
|
||||
PathsD path_solution;
|
||||
|
||||
path_solution = Union(subject_paths, dummy_clip_paths, FillRule::NonZero);
|
||||
|
||||
//path_solution = RamerDouglasPeucker(path_solution, 0.025);
|
||||
|
||||
Vector<Vector<Vector2>> polypaths;
|
||||
|
||||
for (const PathD &scaled_path : path_solution) {
|
||||
Vector<Vector2> shape_outline;
|
||||
for (const PointD &scaled_point : scaled_path) {
|
||||
shape_outline.push_back(Point2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < shape_outline.size(); i++) {
|
||||
shape_outline.write[i] = mesh_instance_xform.xform(shape_outline[i]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
}
|
||||
|
||||
MeshInstance2D::MeshInstance2D() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class NavigationPolygon;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
|
||||
class MeshInstance2D : public Node2D {
|
||||
GDCLASS(MeshInstance2D, Node2D);
|
||||
|
||||
|
|
@ -45,10 +48,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_mesh(const Ref<Mesh> &p_mesh);
|
||||
Ref<Mesh> get_mesh() const;
|
||||
|
|
@ -56,6 +59,14 @@ public:
|
|||
void set_texture(const Ref<Texture2D> &p_texture);
|
||||
Ref<Texture2D> get_texture() const;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
MeshInstance2D();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@
|
|||
|
||||
#include "multimesh_instance_2d.h"
|
||||
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
#include "thirdparty/clipper2/include/clipper2/clipper.h"
|
||||
#include "thirdparty/misc/polypartition.h"
|
||||
|
||||
Callable MultiMeshInstance2D::_navmesh_source_geometry_parsing_callback;
|
||||
RID MultiMeshInstance2D::_navmesh_source_geometry_parser;
|
||||
|
||||
void MultiMeshInstance2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
|
|
@ -84,7 +95,7 @@ Ref<Texture2D> MultiMeshInstance2D::get_texture() const {
|
|||
return texture;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 MultiMeshInstance2D::_edit_get_rect() const {
|
||||
if (multimesh.is_valid()) {
|
||||
AABB aabb = multimesh->get_aabb();
|
||||
|
|
@ -93,7 +104,111 @@ Rect2 MultiMeshInstance2D::_edit_get_rect() const {
|
|||
|
||||
return Node2D::_edit_get_rect();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void MultiMeshInstance2D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&MultiMeshInstance2D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiMeshInstance2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
MultiMeshInstance2D *multimesh_instance = Object::cast_to<MultiMeshInstance2D>(p_node);
|
||||
|
||||
if (multimesh_instance == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
|
||||
if (!(multimesh.is_valid() && multimesh->get_transform_format() == MultiMesh::TRANSFORM_2D)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Mesh> mesh = multimesh->get_mesh();
|
||||
if (mesh.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using namespace Clipper2Lib;
|
||||
|
||||
PathsD mesh_subject_paths, dummy_clip_paths;
|
||||
|
||||
for (int i = 0; i < mesh->get_surface_count(); i++) {
|
||||
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FLAG_USE_2D_VERTICES)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PathD subject_path;
|
||||
|
||||
int index_count = 0;
|
||||
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
|
||||
index_count = mesh->surface_get_array_index_len(i);
|
||||
} else {
|
||||
index_count = mesh->surface_get_array_len(i);
|
||||
}
|
||||
|
||||
ERR_CONTINUE((index_count == 0 || (index_count % 3) != 0));
|
||||
|
||||
Array a = mesh->surface_get_arrays(i);
|
||||
|
||||
Vector<Vector2> mesh_vertices = a[Mesh::ARRAY_VERTEX];
|
||||
|
||||
if (mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_INDEX) {
|
||||
Vector<int> mesh_indices = a[Mesh::ARRAY_INDEX];
|
||||
for (int vertex_index : mesh_indices) {
|
||||
const Vector2 &vertex = mesh_vertices[vertex_index];
|
||||
const PointD &point = PointD(vertex.x, vertex.y);
|
||||
subject_path.push_back(point);
|
||||
}
|
||||
} else {
|
||||
for (const Vector2 &vertex : mesh_vertices) {
|
||||
const PointD &point = PointD(vertex.x, vertex.y);
|
||||
subject_path.push_back(point);
|
||||
}
|
||||
}
|
||||
mesh_subject_paths.push_back(subject_path);
|
||||
}
|
||||
|
||||
PathsD mesh_path_solution = Union(mesh_subject_paths, dummy_clip_paths, FillRule::NonZero);
|
||||
|
||||
//path_solution = RamerDouglasPeucker(path_solution, 0.025);
|
||||
|
||||
int multimesh_instance_count = multimesh->get_visible_instance_count();
|
||||
if (multimesh_instance_count == -1) {
|
||||
multimesh_instance_count = multimesh->get_instance_count();
|
||||
}
|
||||
|
||||
const Transform2D multimesh_instance_xform = p_source_geometry_data->root_node_transform * multimesh_instance->get_global_transform();
|
||||
|
||||
for (int i = 0; i < multimesh_instance_count; i++) {
|
||||
const Transform2D multimesh_instance_mesh_instance_xform = multimesh_instance_xform * multimesh->get_instance_transform_2d(i);
|
||||
|
||||
for (const PathD &mesh_path : mesh_path_solution) {
|
||||
Vector<Vector2> shape_outline;
|
||||
|
||||
for (const PointD &mesh_path_point : mesh_path) {
|
||||
shape_outline.push_back(Point2(static_cast<real_t>(mesh_path_point.x), static_cast<real_t>(mesh_path_point.y)));
|
||||
}
|
||||
|
||||
for (int j = 0; j < shape_outline.size(); j++) {
|
||||
shape_outline.write[j] = multimesh_instance_mesh_instance_xform.xform(shape_outline[j]);
|
||||
}
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MultiMeshInstance2D::MultiMeshInstance2D() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@
|
|||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/resources/multimesh.h"
|
||||
|
||||
class NavigationPolygon;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
|
||||
class MultiMeshInstance2D : public Node2D {
|
||||
GDCLASS(MultiMeshInstance2D, Node2D);
|
||||
|
||||
|
|
@ -46,9 +49,9 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_multimesh(const Ref<MultiMesh> &p_multimesh);
|
||||
Ref<MultiMesh> get_multimesh() const;
|
||||
|
|
@ -56,6 +59,14 @@ public:
|
|||
void set_texture(const Ref<Texture2D> &p_texture);
|
||||
Ref<Texture2D> get_texture() const;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
MultiMeshInstance2D();
|
||||
~MultiMeshInstance2D();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ void NavigationAgent2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,1000,1,or_greater,suffix:px"), "set_path_max_distance", "get_path_max_distance");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered,None"), "set_path_postprocessing", "get_path_postprocessing");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon", PROPERTY_HINT_RANGE, "0.0,10.0,0.001,or_greater,suffix:px"), "set_simplify_epsilon", "get_simplify_epsilon");
|
||||
|
|
@ -253,12 +253,20 @@ void NavigationAgent2D::_notification(int p_what) {
|
|||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_PAUSED: {
|
||||
if (agent_parent) {
|
||||
NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_UNSUSPENDED: {
|
||||
if (get_tree()->is_paused()) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case NOTIFICATION_UNPAUSED: {
|
||||
if (agent_parent) {
|
||||
NavigationServer2D::get_singleton()->agent_set_paused(get_rid(), !agent_parent->can_process());
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
#include "core/math/geometry_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
void NavigationLink2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationLink2D::get_rid);
|
||||
|
|
@ -41,6 +40,9 @@ void NavigationLink2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map);
|
||||
ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional);
|
||||
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional);
|
||||
|
||||
|
|
@ -106,12 +108,7 @@ bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
void NavigationLink2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (enabled) {
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
|
||||
}
|
||||
current_global_transform = get_global_transform();
|
||||
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
|
||||
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
|
||||
_link_enter_navigation_map();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
|
|
@ -120,42 +117,21 @@ void NavigationLink2D::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
set_physics_process_internal(false);
|
||||
if (is_inside_tree()) {
|
||||
Transform2D new_global_transform = get_global_transform();
|
||||
if (current_global_transform != new_global_transform) {
|
||||
current_global_transform = new_global_transform;
|
||||
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
|
||||
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
_link_update_transform();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, RID());
|
||||
_link_exit_navigation_map();
|
||||
} break;
|
||||
case NOTIFICATION_DRAW: {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) {
|
||||
Color color;
|
||||
if (enabled) {
|
||||
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
|
||||
} else {
|
||||
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
|
||||
}
|
||||
|
||||
real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
|
||||
|
||||
draw_line(get_start_position(), get_end_position(), color);
|
||||
draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
|
||||
draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
|
||||
}
|
||||
_update_debug_mesh();
|
||||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 NavigationLink2D::_edit_get_rect() const {
|
||||
if (!is_inside_tree()) {
|
||||
return Rect2();
|
||||
|
|
@ -175,7 +151,7 @@ bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double
|
|||
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment);
|
||||
return p_point.distance_to(closest_point) < p_tolerance;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
RID NavigationLink2D::get_rid() const {
|
||||
return link;
|
||||
|
|
@ -188,15 +164,32 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
|
|||
|
||||
enabled = p_enabled;
|
||||
|
||||
NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
|
||||
NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
|
||||
queue_redraw();
|
||||
}
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void NavigationLink2D::set_navigation_map(RID p_navigation_map) {
|
||||
if (map_override == p_navigation_map) {
|
||||
return;
|
||||
}
|
||||
|
||||
map_override = p_navigation_map;
|
||||
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, map_override);
|
||||
}
|
||||
|
||||
RID NavigationLink2D::get_navigation_map() const {
|
||||
if (map_override.is_valid()) {
|
||||
return map_override;
|
||||
} else if (is_inside_tree()) {
|
||||
return get_world_2d()->get_navigation_map();
|
||||
}
|
||||
return RID();
|
||||
}
|
||||
|
||||
void NavigationLink2D::set_bidirectional(bool p_bidirectional) {
|
||||
if (bidirectional == p_bidirectional) {
|
||||
return;
|
||||
|
|
@ -205,6 +198,10 @@ void NavigationLink2D::set_bidirectional(bool p_bidirectional) {
|
|||
bidirectional = p_bidirectional;
|
||||
|
||||
NavigationServer2D::get_singleton()->link_set_bidirectional(link, bidirectional);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void NavigationLink2D::set_navigation_layers(uint32_t p_navigation_layers) {
|
||||
|
|
@ -255,9 +252,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
|
|||
update_configuration_warnings();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
|
||||
queue_redraw();
|
||||
}
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
|
|
@ -277,9 +272,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
|
|||
update_configuration_warnings();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
|
||||
queue_redraw();
|
||||
}
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +331,7 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) {
|
|||
}
|
||||
|
||||
PackedStringArray NavigationLink2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (start_position.is_equal_approx(end_position)) {
|
||||
warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful."));
|
||||
|
|
@ -347,6 +340,92 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
void NavigationLink2D::_link_enter_navigation_map() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (map_override.is_valid()) {
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, map_override);
|
||||
} else {
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
|
||||
}
|
||||
|
||||
current_global_transform = get_global_transform();
|
||||
|
||||
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
|
||||
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
|
||||
NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
|
||||
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void NavigationLink2D::_link_exit_navigation_map() {
|
||||
NavigationServer2D::get_singleton()->link_set_map(link, RID());
|
||||
}
|
||||
|
||||
void NavigationLink2D::_link_update_transform() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Transform2D new_global_transform = get_global_transform();
|
||||
if (current_global_transform != new_global_transform) {
|
||||
current_global_transform = new_global_transform;
|
||||
NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
|
||||
NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void NavigationLink2D::_update_debug_mesh() {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color color;
|
||||
if (enabled) {
|
||||
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
|
||||
} else {
|
||||
color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
|
||||
}
|
||||
|
||||
real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
|
||||
|
||||
draw_line(get_start_position(), get_end_position(), color);
|
||||
draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
|
||||
draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
|
||||
|
||||
const Vector2 link_segment = end_position - start_position;
|
||||
const float arror_len = 5.0;
|
||||
|
||||
{
|
||||
Vector2 anchor = start_position + (link_segment * 0.75);
|
||||
Vector2 direction = start_position.direction_to(end_position);
|
||||
Vector2 arrow_dir = -direction.orthogonal();
|
||||
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
|
||||
|
||||
arrow_dir = direction.orthogonal();
|
||||
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
|
||||
}
|
||||
|
||||
if (is_bidirectional()) {
|
||||
Vector2 anchor = start_position + (link_segment * 0.25);
|
||||
Vector2 direction = end_position.direction_to(start_position);
|
||||
Vector2 arrow_dir = -direction.orthogonal();
|
||||
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
|
||||
|
||||
arrow_dir = direction.orthogonal();
|
||||
draw_line(anchor, anchor + (arrow_dir - direction) * arror_len, color);
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
NavigationLink2D::NavigationLink2D() {
|
||||
link = NavigationServer2D::get_singleton()->link_create();
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class NavigationLink2D : public Node2D {
|
|||
|
||||
bool enabled = true;
|
||||
RID link;
|
||||
RID map_override;
|
||||
bool bidirectional = true;
|
||||
uint32_t navigation_layers = 1;
|
||||
Vector2 end_position;
|
||||
|
|
@ -47,6 +48,10 @@ class NavigationLink2D : public Node2D {
|
|||
|
||||
Transform2D current_global_transform;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _update_debug_mesh();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
|
@ -57,15 +62,18 @@ protected:
|
|||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
RID get_rid() const;
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
bool is_enabled() const { return enabled; }
|
||||
|
||||
void set_navigation_map(RID p_navigation_map);
|
||||
RID get_navigation_map() const;
|
||||
|
||||
void set_bidirectional(bool p_bidirectional);
|
||||
bool is_bidirectional() const { return bidirectional; }
|
||||
|
||||
|
|
@ -97,6 +105,11 @@ public:
|
|||
|
||||
NavigationLink2D();
|
||||
~NavigationLink2D();
|
||||
|
||||
private:
|
||||
void _link_enter_navigation_map();
|
||||
void _link_exit_navigation_map();
|
||||
void _link_update_transform();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_LINK_2D_H
|
||||
|
|
|
|||
|
|
@ -31,9 +31,13 @@
|
|||
#include "navigation_obstacle_2d.h"
|
||||
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
Callable NavigationObstacle2D::_navmesh_source_geometry_parsing_callback;
|
||||
RID NavigationObstacle2D::_navmesh_source_geometry_parser;
|
||||
|
||||
void NavigationObstacle2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle2D::get_rid);
|
||||
|
|
@ -89,7 +93,7 @@ void NavigationObstacle2D::_notification(int p_what) {
|
|||
previous_transform = get_global_transform();
|
||||
// need to trigger map controlled agent assignment somehow for the fake_agent since obstacles use no callback like regular agents
|
||||
NavigationServer2D::get_singleton()->obstacle_set_avoidance_enabled(obstacle, avoidance_enabled);
|
||||
_update_position(get_global_position());
|
||||
_update_transform();
|
||||
set_physics_process_internal(true);
|
||||
#ifdef DEBUG_ENABLED
|
||||
RS::get_singleton()->canvas_item_set_parent(debug_canvas_item, get_world_2d()->get_canvas());
|
||||
|
|
@ -104,6 +108,7 @@ void NavigationObstacle2D::_notification(int p_what) {
|
|||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_PAUSED: {
|
||||
if (!can_process()) {
|
||||
map_before_pause = map_current;
|
||||
|
|
@ -115,6 +120,13 @@ void NavigationObstacle2D::_notification(int p_what) {
|
|||
NavigationServer2D::get_singleton()->obstacle_set_paused(obstacle, !can_process());
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_UNSUSPENDED: {
|
||||
if (get_tree()->is_paused()) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case NOTIFICATION_UNPAUSED: {
|
||||
if (!can_process()) {
|
||||
map_before_pause = map_current;
|
||||
|
|
@ -134,7 +146,7 @@ void NavigationObstacle2D::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (is_inside_tree()) {
|
||||
_update_position(get_global_position());
|
||||
_update_transform();
|
||||
|
||||
if (velocity_submitted) {
|
||||
velocity_submitted = false;
|
||||
|
|
@ -159,8 +171,7 @@ void NavigationObstacle2D::_notification(int p_what) {
|
|||
|
||||
if (is_debug_enabled) {
|
||||
RS::get_singleton()->canvas_item_clear(debug_canvas_item);
|
||||
Transform2D debug_transform = Transform2D(0.0, get_global_position());
|
||||
RS::get_singleton()->canvas_item_set_transform(debug_canvas_item, debug_transform);
|
||||
RS::get_singleton()->canvas_item_set_transform(debug_canvas_item, Transform2D());
|
||||
_update_fake_agent_radius_debug();
|
||||
_update_static_obstacle_debug();
|
||||
}
|
||||
|
|
@ -180,6 +191,7 @@ NavigationObstacle2D::NavigationObstacle2D() {
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
debug_canvas_item = RenderingServer::get_singleton()->canvas_item_create();
|
||||
debug_mesh_rid = RenderingServer::get_singleton()->mesh_create();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +202,10 @@ NavigationObstacle2D::~NavigationObstacle2D() {
|
|||
obstacle = RID();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (debug_mesh_rid.is_valid()) {
|
||||
RenderingServer::get_singleton()->free(debug_mesh_rid);
|
||||
debug_mesh_rid = RID();
|
||||
}
|
||||
if (debug_canvas_item.is_valid()) {
|
||||
RenderingServer::get_singleton()->free(debug_canvas_item);
|
||||
debug_canvas_item = RID();
|
||||
|
|
@ -199,7 +215,12 @@ NavigationObstacle2D::~NavigationObstacle2D() {
|
|||
|
||||
void NavigationObstacle2D::set_vertices(const Vector<Vector2> &p_vertices) {
|
||||
vertices = p_vertices;
|
||||
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, vertices);
|
||||
|
||||
vertices_are_clockwise = !Geometry2D::is_polygon_clockwise(vertices); // Geometry2D is inverted.
|
||||
vertices_are_valid = !Geometry2D::triangulate_polygon(vertices).is_empty();
|
||||
|
||||
const Transform2D node_transform = is_inside_tree() ? get_global_transform() : Transform2D();
|
||||
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, node_transform.xform(vertices));
|
||||
#ifdef DEBUG_ENABLED
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
|
@ -230,7 +251,8 @@ void NavigationObstacle2D::set_radius(real_t p_radius) {
|
|||
|
||||
radius = p_radius;
|
||||
|
||||
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, radius);
|
||||
const Vector2 safe_scale = (is_inside_tree() ? get_global_scale() : get_scale()).abs().maxf(0.001);
|
||||
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, safe_scale[safe_scale.max_axis_index()] * radius);
|
||||
#ifdef DEBUG_ENABLED
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
|
@ -303,6 +325,94 @@ bool NavigationObstacle2D::get_carve_navigation_mesh() const {
|
|||
return carve_navigation_mesh;
|
||||
}
|
||||
|
||||
PackedStringArray NavigationObstacle2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
const Vector2 global_scale = get_global_scale();
|
||||
if (global_scale.x < 0.001 || global_scale.y < 0.001) {
|
||||
warnings.push_back(RTR("NavigationObstacle2D does not support negative or zero scaling."));
|
||||
}
|
||||
|
||||
if (radius > 0.0 && !get_global_transform().is_conformal()) {
|
||||
warnings.push_back(RTR("The agent radius can only be scaled uniformly. The largest value along the two axes of the global scale will be used to scale the radius. This value may change in unexpected ways when the node is rotated."));
|
||||
}
|
||||
|
||||
if (radius > 0.0 && get_global_skew() != 0.0) {
|
||||
warnings.push_back(RTR("Skew has no effect on the agent radius."));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&NavigationObstacle2D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
NavigationObstacle2D *obstacle = Object::cast_to<NavigationObstacle2D>(p_node);
|
||||
|
||||
if (obstacle == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obstacle->get_affect_navigation_mesh()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector2 safe_scale = obstacle->get_global_scale().abs().maxf(0.001);
|
||||
const float obstacle_radius = obstacle->get_radius();
|
||||
|
||||
if (obstacle_radius > 0.0) {
|
||||
// Radius defined obstacle should be uniformly scaled from obstacle basis max scale axis.
|
||||
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
|
||||
const Vector2 uniform_max_scale = Vector2(scaling_max_value, scaling_max_value);
|
||||
const Transform2D obstacle_circle_transform = p_source_geometry_data->root_node_transform * Transform2D(obstacle->get_global_rotation(), uniform_max_scale, 0.0, obstacle->get_global_position());
|
||||
|
||||
Vector<Vector2> obstruction_circle_vertices;
|
||||
|
||||
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
|
||||
// Without, navigation paths can go directly through the middle of the obstacle and conflict with the avoidance to get agents stuck.
|
||||
// No place for excessive "round" detail here. Every additional edge adds a high cost for something that needs to be quick, not pretty.
|
||||
static const int circle_points = 12;
|
||||
|
||||
obstruction_circle_vertices.resize(circle_points);
|
||||
Vector2 *circle_vertices_ptrw = obstruction_circle_vertices.ptrw();
|
||||
const real_t circle_point_step = Math_TAU / circle_points;
|
||||
|
||||
for (int i = 0; i < circle_points; i++) {
|
||||
const float angle = i * circle_point_step;
|
||||
circle_vertices_ptrw[i] = obstacle_circle_transform.xform(Vector2(Math::cos(angle) * obstacle_radius, Math::sin(angle) * obstacle_radius));
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_carve_navigation_mesh());
|
||||
}
|
||||
|
||||
// Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account.
|
||||
const Transform2D node_xform = p_source_geometry_data->root_node_transform * obstacle->get_global_transform();
|
||||
|
||||
const Vector<Vector2> &obstacle_vertices = obstacle->get_vertices();
|
||||
|
||||
if (obstacle_vertices.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<Vector2> obstruction_shape_vertices;
|
||||
obstruction_shape_vertices.resize(obstacle_vertices.size());
|
||||
|
||||
const Vector2 *obstacle_vertices_ptr = obstacle_vertices.ptr();
|
||||
Vector2 *obstruction_shape_vertices_ptrw = obstruction_shape_vertices.ptrw();
|
||||
|
||||
for (int i = 0; i < obstacle_vertices.size(); i++) {
|
||||
obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]);
|
||||
}
|
||||
p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_carve_navigation_mesh());
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::_update_map(RID p_map) {
|
||||
map_current = p_map;
|
||||
NavigationServer2D::get_singleton()->obstacle_set_map(obstacle, p_map);
|
||||
|
|
@ -315,54 +425,88 @@ void NavigationObstacle2D::_update_position(const Vector2 p_position) {
|
|||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
void NavigationObstacle2D::_update_transform() {
|
||||
_update_position(get_global_position());
|
||||
// Prevent non-positive or non-uniform scaling of dynamic obstacle radius.
|
||||
const Vector2 safe_scale = get_global_scale().abs().maxf(0.001);
|
||||
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
|
||||
NavigationServer2D::get_singleton()->obstacle_set_radius(obstacle, scaling_max_value * radius);
|
||||
NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle, get_global_transform().translated(-get_global_position()).xform(vertices));
|
||||
#ifdef DEBUG_ENABLED
|
||||
queue_redraw();
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void NavigationObstacle2D::_update_fake_agent_radius_debug() {
|
||||
if (radius > 0.0 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_radius()) {
|
||||
Color debug_radius_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_obstacles_radius_color();
|
||||
|
||||
RS::get_singleton()->canvas_item_add_circle(debug_canvas_item, Vector2(), radius, debug_radius_color);
|
||||
// Prevent non-positive scaling.
|
||||
const Vector2 safe_scale = get_global_scale().abs().maxf(0.001);
|
||||
// Agent radius is a scalar value and does not support non-uniform scaling, choose the largest axis.
|
||||
const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
|
||||
RS::get_singleton()->canvas_item_add_circle(debug_canvas_item, get_global_position(), scaling_max_value * radius, debug_radius_color);
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void NavigationObstacle2D::_update_static_obstacle_debug() {
|
||||
if (get_vertices().size() > 2 && NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
|
||||
bool obstacle_pushes_inward = Geometry2D::is_polygon_clockwise(get_vertices());
|
||||
|
||||
Color debug_static_obstacle_face_color;
|
||||
|
||||
if (obstacle_pushes_inward) {
|
||||
debug_static_obstacle_face_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_face_color();
|
||||
} else {
|
||||
debug_static_obstacle_face_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_face_color();
|
||||
}
|
||||
|
||||
Vector<Vector2> debug_obstacle_polygon_vertices = get_vertices();
|
||||
|
||||
Vector<Color> debug_obstacle_polygon_colors;
|
||||
debug_obstacle_polygon_colors.resize(debug_obstacle_polygon_vertices.size());
|
||||
debug_obstacle_polygon_colors.fill(debug_static_obstacle_face_color);
|
||||
|
||||
RS::get_singleton()->canvas_item_add_polygon(debug_canvas_item, debug_obstacle_polygon_vertices, debug_obstacle_polygon_colors);
|
||||
|
||||
Color debug_static_obstacle_edge_color;
|
||||
|
||||
if (obstacle_pushes_inward) {
|
||||
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
|
||||
} else {
|
||||
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
|
||||
}
|
||||
|
||||
Vector<Vector2> debug_obstacle_line_vertices = get_vertices();
|
||||
debug_obstacle_line_vertices.push_back(debug_obstacle_line_vertices[0]);
|
||||
debug_obstacle_line_vertices.resize(debug_obstacle_line_vertices.size());
|
||||
|
||||
Vector<Color> debug_obstacle_line_colors;
|
||||
debug_obstacle_line_colors.resize(debug_obstacle_line_vertices.size());
|
||||
debug_obstacle_line_colors.fill(debug_static_obstacle_edge_color);
|
||||
|
||||
RS::get_singleton()->canvas_item_add_polyline(debug_canvas_item, debug_obstacle_line_vertices, debug_obstacle_line_colors, 4.0);
|
||||
if (get_vertices().size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_enable_obstacles_static()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderingServer *rs = RenderingServer::get_singleton();
|
||||
|
||||
rs->mesh_clear(debug_mesh_rid);
|
||||
|
||||
const int vertex_count = vertices.size();
|
||||
|
||||
Vector<Vector2> edge_vertex_array;
|
||||
edge_vertex_array.resize(vertex_count * 4);
|
||||
|
||||
Vector2 *edge_vertex_array_ptrw = edge_vertex_array.ptrw();
|
||||
|
||||
int vertex_index = 0;
|
||||
|
||||
for (int i = 0; i < vertex_count; i++) {
|
||||
Vector2 point = vertices[i];
|
||||
Vector2 next_point = vertices[(i + 1) % vertex_count];
|
||||
|
||||
Vector2 direction = next_point.direction_to(point);
|
||||
Vector2 arrow_dir = -direction.orthogonal();
|
||||
Vector2 edge_middle = point + ((next_point - point) * 0.5);
|
||||
|
||||
edge_vertex_array_ptrw[vertex_index++] = edge_middle;
|
||||
edge_vertex_array_ptrw[vertex_index++] = edge_middle + (arrow_dir * 10.0);
|
||||
|
||||
edge_vertex_array_ptrw[vertex_index++] = point;
|
||||
edge_vertex_array_ptrw[vertex_index++] = next_point;
|
||||
}
|
||||
|
||||
Color debug_static_obstacle_edge_color;
|
||||
|
||||
if (are_vertices_valid()) {
|
||||
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushout_edge_color();
|
||||
} else {
|
||||
debug_static_obstacle_edge_color = NavigationServer2D::get_singleton()->get_debug_navigation_avoidance_static_obstacle_pushin_edge_color();
|
||||
}
|
||||
|
||||
Vector<Color> line_color_array;
|
||||
line_color_array.resize(edge_vertex_array.size());
|
||||
line_color_array.fill(debug_static_obstacle_edge_color);
|
||||
|
||||
Array edge_mesh_array;
|
||||
edge_mesh_array.resize(Mesh::ARRAY_MAX);
|
||||
edge_mesh_array[Mesh::ARRAY_VERTEX] = edge_vertex_array;
|
||||
edge_mesh_array[Mesh::ARRAY_COLOR] = line_color_array;
|
||||
|
||||
rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_LINES, edge_mesh_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
|
||||
|
||||
rs->canvas_item_add_mesh(debug_canvas_item, debug_mesh_rid, get_global_transform());
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class NavigationPolygon;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
|
||||
class NavigationObstacle2D : public Node2D {
|
||||
GDCLASS(NavigationObstacle2D, Node2D);
|
||||
|
||||
|
|
@ -44,6 +47,8 @@ class NavigationObstacle2D : public Node2D {
|
|||
real_t radius = 0.0;
|
||||
|
||||
Vector<Vector2> vertices;
|
||||
bool vertices_are_clockwise = true;
|
||||
bool vertices_are_valid = true;
|
||||
|
||||
bool avoidance_enabled = true;
|
||||
uint32_t avoidance_layers = 1;
|
||||
|
|
@ -60,6 +65,8 @@ class NavigationObstacle2D : public Node2D {
|
|||
#ifdef DEBUG_ENABLED
|
||||
private:
|
||||
RID debug_canvas_item;
|
||||
RID debug_mesh_rid;
|
||||
|
||||
void _update_fake_agent_radius_debug();
|
||||
void _update_static_obstacle_debug();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
|
@ -84,7 +91,10 @@ public:
|
|||
real_t get_radius() const { return radius; }
|
||||
|
||||
void set_vertices(const Vector<Vector2> &p_vertices);
|
||||
const Vector<Vector2> &get_vertices() const { return vertices; };
|
||||
const Vector<Vector2> &get_vertices() const { return vertices; }
|
||||
|
||||
bool are_vertices_clockwise() const { return vertices_are_clockwise; }
|
||||
bool are_vertices_valid() const { return vertices_are_valid; }
|
||||
|
||||
void set_avoidance_layers(uint32_t p_layers);
|
||||
uint32_t get_avoidance_layers() const;
|
||||
|
|
@ -96,7 +106,7 @@ public:
|
|||
bool get_avoidance_layer_value(int p_layer_number) const;
|
||||
|
||||
void set_velocity(const Vector2 p_velocity);
|
||||
Vector2 get_velocity() const { return velocity; };
|
||||
Vector2 get_velocity() const { return velocity; }
|
||||
|
||||
void _avoidance_done(Vector3 p_new_velocity); // Dummy
|
||||
|
||||
|
|
@ -106,9 +116,20 @@ public:
|
|||
void set_carve_navigation_mesh(bool p_enabled);
|
||||
bool get_carve_navigation_mesh() const;
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
private:
|
||||
void _update_map(RID p_map);
|
||||
void _update_position(const Vector2 p_position);
|
||||
void _update_transform();
|
||||
};
|
||||
|
||||
#endif // NAVIGATION_OBSTACLE_2D_H
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "navigation_region_2d.h"
|
||||
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
|
|
@ -142,7 +141,7 @@ RID NavigationRegion2D::get_region_rid() const {
|
|||
return get_rid();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 NavigationRegion2D::_edit_get_rect() const {
|
||||
return navigation_polygon.is_valid() ? navigation_polygon->_edit_get_rect() : Rect2();
|
||||
}
|
||||
|
|
@ -150,7 +149,7 @@ Rect2 NavigationRegion2D::_edit_get_rect() const {
|
|||
bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
return navigation_polygon.is_valid() ? navigation_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void NavigationRegion2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
|
|
@ -164,16 +163,14 @@ void NavigationRegion2D::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (debug_instance_rid.is_valid()) {
|
||||
RS::get_singleton()->canvas_item_set_visible(debug_instance_rid, is_visible_in_tree());
|
||||
}
|
||||
_set_debug_visible(is_visible_in_tree());
|
||||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
_region_exit_navigation_map();
|
||||
#ifdef DEBUG_ENABLED
|
||||
_free_debug();
|
||||
_set_debug_visible(false);
|
||||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
|
||||
|
|
@ -203,11 +200,21 @@ void NavigationRegion2D::set_navigation_polygon(const Ref<NavigationPolygon> &p_
|
|||
#ifdef DEBUG_ENABLED
|
||||
debug_mesh_dirty = true;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
_update_bounds();
|
||||
|
||||
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, p_navigation_polygon);
|
||||
|
||||
if (navigation_polygon.is_valid()) {
|
||||
navigation_polygon->connect_changed(callable_mp(this, &NavigationRegion2D::_navigation_polygon_changed));
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (navigation_polygon.is_null()) {
|
||||
_set_debug_visible(false);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
_navigation_polygon_changed();
|
||||
|
||||
update_configuration_warnings();
|
||||
|
|
@ -238,7 +245,7 @@ RID NavigationRegion2D::get_navigation_map() const {
|
|||
|
||||
void NavigationRegion2D::bake_navigation_polygon(bool p_on_thread) {
|
||||
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "The SceneTree can only be parsed on the main thread. Call this function from the main thread or use call_deferred().");
|
||||
ERR_FAIL_COND_MSG(!navigation_polygon.is_valid(), "Baking the navigation polygon requires a valid `NavigationPolygon` resource.");
|
||||
ERR_FAIL_COND_MSG(navigation_polygon.is_null(), "Baking the navigation polygon requires a valid `NavigationPolygon` resource.");
|
||||
|
||||
Ref<NavigationMeshSourceGeometryData2D> source_geometry_data;
|
||||
source_geometry_data.instantiate();
|
||||
|
|
@ -295,7 +302,7 @@ PackedStringArray NavigationRegion2D::get_configuration_warnings() const {
|
|||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (is_visible_in_tree() && is_inside_tree()) {
|
||||
if (!navigation_polygon.is_valid()) {
|
||||
if (navigation_polygon.is_null()) {
|
||||
warnings.push_back(RTR("A NavigationMesh resource must be set or created for this node to work. Please set a property or draw a polygon."));
|
||||
}
|
||||
}
|
||||
|
|
@ -337,6 +344,8 @@ void NavigationRegion2D::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("_navigation_polygon_changed"), &NavigationRegion2D::_navigation_polygon_changed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationRegion2D::get_bounds);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navigation_polygon", PROPERTY_HINT_RESOURCE_TYPE, "NavigationPolygon"), "set_navigation_polygon", "get_navigation_polygon");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_edge_connections"), "set_use_edge_connections", "get_use_edge_connections");
|
||||
|
|
@ -392,6 +401,12 @@ NavigationRegion2D::~NavigationRegion2D() {
|
|||
#ifdef DEBUG_ENABLED
|
||||
NavigationServer2D::get_singleton()->disconnect(SNAME("map_changed"), callable_mp(this, &NavigationRegion2D::_navigation_map_changed));
|
||||
NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion2D::_navigation_debug_changed));
|
||||
if (debug_instance_rid.is_valid()) {
|
||||
RS::get_singleton()->free(debug_instance_rid);
|
||||
}
|
||||
if (debug_mesh_rid.is_valid()) {
|
||||
RS::get_singleton()->free(debug_mesh_rid);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +450,7 @@ void NavigationRegion2D::_region_update_transform() {
|
|||
#ifdef DEBUG_ENABLED
|
||||
void NavigationRegion2D::_update_debug_mesh() {
|
||||
if (!is_inside_tree()) {
|
||||
_free_debug();
|
||||
_set_debug_visible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -452,6 +467,7 @@ void NavigationRegion2D::_update_debug_mesh() {
|
|||
const Transform2D region_gt = get_global_transform();
|
||||
|
||||
rs->canvas_item_set_parent(debug_instance_rid, get_world_2d()->get_canvas());
|
||||
rs->canvas_item_set_z_index(debug_instance_rid, RS::CANVAS_ITEM_Z_MAX - 2);
|
||||
rs->canvas_item_set_transform(debug_instance_rid, region_gt);
|
||||
|
||||
if (!debug_mesh_dirty) {
|
||||
|
|
@ -629,17 +645,34 @@ void NavigationRegion2D::_update_debug_baking_rect() {
|
|||
#endif // DEBUG_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void NavigationRegion2D::_free_debug() {
|
||||
void NavigationRegion2D::_set_debug_visible(bool p_visible) {
|
||||
RenderingServer *rs = RenderingServer::get_singleton();
|
||||
ERR_FAIL_NULL(rs);
|
||||
if (debug_instance_rid.is_valid()) {
|
||||
rs->canvas_item_clear(debug_instance_rid);
|
||||
rs->free(debug_instance_rid);
|
||||
debug_instance_rid = RID();
|
||||
}
|
||||
if (debug_mesh_rid.is_valid()) {
|
||||
rs->free(debug_mesh_rid);
|
||||
debug_mesh_rid = RID();
|
||||
RS::get_singleton()->canvas_item_set_visible(debug_instance_rid, p_visible);
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void NavigationRegion2D::_update_bounds() {
|
||||
if (navigation_polygon.is_null()) {
|
||||
bounds = Rect2();
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector<Vector2> &vertices = navigation_polygon->get_vertices();
|
||||
if (vertices.is_empty()) {
|
||||
bounds = Rect2();
|
||||
return;
|
||||
}
|
||||
|
||||
const Transform2D gt = is_inside_tree() ? get_global_transform() : get_transform();
|
||||
|
||||
Rect2 new_bounds;
|
||||
new_bounds.position = gt.xform(vertices[0]);
|
||||
|
||||
for (const Vector2 &vertex : vertices) {
|
||||
new_bounds.expand_to(gt.xform(vertex));
|
||||
}
|
||||
bounds = new_bounds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ class NavigationRegion2D : public Node2D {
|
|||
|
||||
void _navigation_polygon_changed();
|
||||
|
||||
Rect2 bounds;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
private:
|
||||
RID debug_mesh_rid;
|
||||
|
|
@ -57,7 +59,7 @@ private:
|
|||
|
||||
bool debug_mesh_dirty = true;
|
||||
|
||||
void _free_debug();
|
||||
void _set_debug_visible(bool p_visible);
|
||||
void _update_debug_mesh();
|
||||
void _update_debug_edge_connections_mesh();
|
||||
void _update_debug_baking_rect();
|
||||
|
|
@ -75,10 +77,10 @@ protected:
|
|||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
RID get_rid() const;
|
||||
|
||||
void set_enabled(bool p_enabled);
|
||||
|
|
@ -113,10 +115,13 @@ public:
|
|||
void _bake_finished(Ref<NavigationPolygon> p_navigation_polygon);
|
||||
bool is_baking() const;
|
||||
|
||||
Rect2 get_bounds() const { return bounds; }
|
||||
|
||||
NavigationRegion2D();
|
||||
~NavigationRegion2D();
|
||||
|
||||
private:
|
||||
void _update_bounds();
|
||||
void _region_enter_navigation_map();
|
||||
void _region_exit_navigation_map();
|
||||
void _region_update_transform();
|
||||
|
|
|
|||
|
|
@ -374,7 +374,9 @@ void Node2D::set_transform(const Transform2D &p_transform) {
|
|||
transform = p_transform;
|
||||
_set_xform_dirty(true);
|
||||
|
||||
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
|
||||
if (!_is_using_identity_transform()) {
|
||||
RenderingServer::get_singleton()->canvas_item_set_transform(get_canvas_item(), transform);
|
||||
}
|
||||
|
||||
_notify_transform();
|
||||
}
|
||||
|
|
@ -487,7 +489,7 @@ void Node2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent);
|
||||
|
||||
ADD_GROUP("Transform", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_less,or_greater,hide_slider,suffix:px"), "set_position", "get_position");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_RANGE, "-99999,99999,or_less,or_greater,hide_slider,suffix:px"), "set_position", "get_position");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale");
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "parallax_2d.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void Parallax2D::_notification(int p_what) {
|
||||
|
|
@ -47,9 +46,18 @@ void Parallax2D::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
autoscroll_offset += autoscroll * get_process_delta_time();
|
||||
autoscroll_offset = autoscroll_offset.posmodv(repeat_size);
|
||||
Point2 offset = scroll_offset;
|
||||
offset += autoscroll * get_process_delta_time();
|
||||
|
||||
if (repeat_size.x) {
|
||||
offset.x = Math::fposmod(offset.x, repeat_size.x);
|
||||
}
|
||||
|
||||
if (repeat_size.y) {
|
||||
offset.y = Math::fposmod(offset.y, repeat_size.y);
|
||||
}
|
||||
|
||||
scroll_offset = offset;
|
||||
_update_scroll();
|
||||
} break;
|
||||
|
||||
|
|
@ -61,7 +69,9 @@ void Parallax2D::_notification(int p_what) {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void Parallax2D::_edit_set_position(const Point2 &p_position) {
|
||||
set_scroll_offset(p_position);
|
||||
// Avoids early return for grid snap compatibility
|
||||
scroll_offset = p_position;
|
||||
_update_scroll();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
|
|
@ -74,7 +84,11 @@ void Parallax2D::_validate_property(PropertyInfo &p_property) const {
|
|||
void Parallax2D::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset, const Point2 &p_adj_screen_pos) {
|
||||
if (!ignore_camera_scroll) {
|
||||
if (get_viewport() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
|
||||
set_screen_offset((p_adj_screen_pos + Vector2(0.5, 0.5)).floor());
|
||||
Size2 vps = get_viewport_rect().size;
|
||||
Vector2 offset;
|
||||
offset.x = ((int)vps.width % 2) ? 0.0 : 0.5;
|
||||
offset.y = ((int)vps.height % 2) ? 0.0 : 0.5;
|
||||
set_screen_offset((p_adj_screen_pos + offset).floor());
|
||||
} else {
|
||||
set_screen_offset(p_adj_screen_pos);
|
||||
}
|
||||
|
|
@ -106,14 +120,14 @@ void Parallax2D::_update_scroll() {
|
|||
scroll_ofs *= scroll_scale;
|
||||
|
||||
if (repeat_size.x) {
|
||||
real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x * get_scale().x);
|
||||
real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x, repeat_size.x * get_scale().x);
|
||||
scroll_ofs.x = screen_offset.x - mod;
|
||||
} else {
|
||||
scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x;
|
||||
}
|
||||
|
||||
if (repeat_size.y) {
|
||||
real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y * get_scale().y);
|
||||
real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y, repeat_size.y * get_scale().y);
|
||||
scroll_ofs.y = screen_offset.y - mod;
|
||||
} else {
|
||||
scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y;
|
||||
|
|
@ -193,7 +207,6 @@ void Parallax2D::set_autoscroll(const Point2 &p_autoscroll) {
|
|||
}
|
||||
|
||||
autoscroll = p_autoscroll;
|
||||
autoscroll_offset = Point2();
|
||||
|
||||
_update_process();
|
||||
_update_scroll();
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class Parallax2D : public Node2D {
|
|||
Point2 limit_begin = Point2(-DEFAULT_LIMIT, -DEFAULT_LIMIT);
|
||||
Point2 limit_end = Point2(DEFAULT_LIMIT, DEFAULT_LIMIT);
|
||||
Point2 autoscroll;
|
||||
Point2 autoscroll_offset;
|
||||
bool follow_viewport = true;
|
||||
bool ignore_camera_scroll = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -78,13 +78,7 @@ void ParallaxLayer::_update_mirroring() {
|
|||
}
|
||||
|
||||
void ParallaxLayer::set_mirroring(const Size2 &p_mirroring) {
|
||||
mirroring = p_mirroring;
|
||||
if (mirroring.x < 0) {
|
||||
mirroring.x = 0;
|
||||
}
|
||||
if (mirroring.y < 0) {
|
||||
mirroring.y = 0;
|
||||
}
|
||||
mirroring = p_mirroring.maxf(0);
|
||||
|
||||
_update_mirroring();
|
||||
}
|
||||
|
|
@ -139,7 +133,7 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s
|
|||
}
|
||||
|
||||
PackedStringArray ParallaxLayer::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
|
||||
warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@
|
|||
#include "editor/themes/editor_scale.h"
|
||||
#endif
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 Path2D::_edit_get_rect() const {
|
||||
if (!curve.is_valid() || curve->get_point_count() == 0) {
|
||||
if (curve.is_null() || curve->get_point_count() == 0) {
|
||||
return Rect2(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ void Path2D::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
// Draw the curve if path debugging is enabled.
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (!curve.is_valid()) {
|
||||
if (curve.is_null()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -138,13 +138,14 @@ void Path2D::_notification(int p_what) {
|
|||
draw_polyline(v2p, get_tree()->get_debug_paths_color(), line_width, false);
|
||||
}
|
||||
|
||||
// Draw fish bones
|
||||
// Draw fish bone every 4 points to reduce visual noise and performance impact
|
||||
// (compared to drawing it for every point).
|
||||
{
|
||||
PackedVector2Array v2p;
|
||||
v2p.resize(3);
|
||||
Vector2 *w = v2p.ptrw();
|
||||
|
||||
for (int i = 0; i < sample_count; i++) {
|
||||
for (int i = 0; i < sample_count; i += 4) {
|
||||
const Vector2 p = r[i].get_origin();
|
||||
const Vector2 side = r[i].columns[1];
|
||||
const Vector2 forward = r[i].columns[0];
|
||||
|
|
@ -167,17 +168,16 @@ void Path2D::_curve_changed() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_paths_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
PathFollow2D *follow = Object::cast_to<PathFollow2D>(get_child(i));
|
||||
if (follow) {
|
||||
follow->path_changed();
|
||||
}
|
||||
}
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_paths_hint()) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void Path2D::set_curve(const Ref<Curve2D> &p_curve) {
|
||||
|
|
@ -221,7 +221,7 @@ void PathFollow2D::_update_transform() {
|
|||
}
|
||||
|
||||
Ref<Curve2D> c = path->get_curve();
|
||||
if (!c.is_valid()) {
|
||||
if (c.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +288,7 @@ void PathFollow2D::_validate_property(PropertyInfo &p_property) const {
|
|||
}
|
||||
|
||||
PackedStringArray PathFollow2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (is_visible_in_tree() && is_inside_tree()) {
|
||||
if (!Object::cast_to<Path2D>(get_parent())) {
|
||||
|
|
@ -378,9 +378,10 @@ real_t PathFollow2D::get_progress() const {
|
|||
}
|
||||
|
||||
void PathFollow2D::set_progress_ratio(real_t p_ratio) {
|
||||
if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
|
||||
set_progress(p_ratio * path->get_curve()->get_baked_length());
|
||||
}
|
||||
ERR_FAIL_NULL_MSG(path, "Can only set progress ratio on a PathFollow2D that is the child of a Path2D which is itself part of the scene tree.");
|
||||
ERR_FAIL_COND_MSG(path->get_curve().is_null(), "Can't set progress ratio on a PathFollow2D that does not have a Curve.");
|
||||
ERR_FAIL_COND_MSG(!path->get_curve()->get_baked_length(), "Can't set progress ratio on a PathFollow2D that has a 0 length curve.");
|
||||
set_progress(p_ratio * path->get_curve()->get_baked_length());
|
||||
}
|
||||
|
||||
real_t PathFollow2D::get_progress_ratio() const {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -352,14 +352,14 @@ void CharacterBody2D::_apply_floor_snap(bool p_wall_as_floor) {
|
|||
floor_normal = result.collision_normal;
|
||||
_set_platform_data(result);
|
||||
|
||||
if (floor_stop_on_slope) {
|
||||
// move and collide may stray the object a bit because of pre un-stucking,
|
||||
// so only ensure that motion happens on floor direction in this case.
|
||||
if (result.travel.length() > margin) {
|
||||
result.travel = up_direction * up_direction.dot(result.travel);
|
||||
} else {
|
||||
result.travel = Vector2();
|
||||
}
|
||||
// Ensure that we only move the body along the up axis, because
|
||||
// move_and_collide may stray the object a bit when getting it unstuck.
|
||||
// Canceling this motion should not affect move_and_slide, as previous
|
||||
// calls to move_and_collide already took care of freeing the body.
|
||||
if (result.travel.length() > margin) {
|
||||
result.travel = up_direction * up_direction.dot(result.travel);
|
||||
} else {
|
||||
result.travel = Vector2();
|
||||
}
|
||||
|
||||
parameters.from.columns[2] += result.travel;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ void CollisionObject2D::_notification(int p_what) {
|
|||
|
||||
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
|
||||
Ref<World2D> world_ref = get_world_2d();
|
||||
ERR_FAIL_COND(!world_ref.is_valid());
|
||||
ERR_FAIL_COND(world_ref.is_null());
|
||||
RID space = world_ref->get_space();
|
||||
if (area) {
|
||||
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
|
||||
|
|
@ -582,7 +582,7 @@ void CollisionObject2D::_update_pickable() {
|
|||
}
|
||||
|
||||
PackedStringArray CollisionObject2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (shapes.is_empty()) {
|
||||
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@
|
|||
#include "scene/resources/2d/concave_polygon_shape_2d.h"
|
||||
#include "scene/resources/2d/convex_polygon_shape_2d.h"
|
||||
|
||||
#include "thirdparty/misc/polypartition.h"
|
||||
|
||||
void CollisionPolygon2D::_build_polygon() {
|
||||
collision_object->shape_owner_clear_shapes(owner_id);
|
||||
|
||||
|
|
@ -217,7 +215,7 @@ CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
|
|||
return build_mode;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 CollisionPolygon2D::_edit_get_rect() const {
|
||||
return aabb;
|
||||
}
|
||||
|
|
@ -232,7 +230,7 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
|
|||
#endif
|
||||
|
||||
PackedStringArray CollisionPolygon2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
|
||||
warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
|
|
|
|||
|
|
@ -49,11 +49,6 @@ void CollisionShape2D::_update_in_shape_owner(bool p_xform_only) {
|
|||
collision_object->shape_owner_set_one_way_collision_margin(owner_id, one_way_collision_margin);
|
||||
}
|
||||
|
||||
Color CollisionShape2D::_get_default_debug_color() const {
|
||||
SceneTree *st = SceneTree::get_singleton();
|
||||
return st ? st->get_debug_collisions_color() : Color();
|
||||
}
|
||||
|
||||
void CollisionShape2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_PARENTED: {
|
||||
|
|
@ -94,7 +89,7 @@ void CollisionShape2D::_notification(int p_what) {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!shape.is_valid()) {
|
||||
if (shape.is_null()) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +161,7 @@ Ref<Shape2D> CollisionShape2D::get_shape() const {
|
|||
}
|
||||
|
||||
bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
if (!shape.is_valid()) {
|
||||
if (shape.is_null()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -174,13 +169,13 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double
|
|||
}
|
||||
|
||||
PackedStringArray CollisionShape2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
CollisionObject2D *col_object = Object::cast_to<CollisionObject2D>(get_parent());
|
||||
if (col_object == nullptr) {
|
||||
warnings.push_back(RTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node.\nPlease only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
|
||||
}
|
||||
if (!shape.is_valid()) {
|
||||
if (shape.is_null()) {
|
||||
warnings.push_back(RTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"));
|
||||
}
|
||||
if (one_way_collision && Object::cast_to<Area2D>(col_object)) {
|
||||
|
|
@ -190,7 +185,7 @@ PackedStringArray CollisionShape2D::get_configuration_warnings() const {
|
|||
Ref<ConvexPolygonShape2D> convex = shape;
|
||||
Ref<ConcavePolygonShape2D> concave = shape;
|
||||
if (convex.is_valid() || concave.is_valid()) {
|
||||
warnings.push_back(RTR("Polygon-based shapes are not meant be used nor edited directly through the CollisionShape2D node. Please use the CollisionPolygon2D node instead."));
|
||||
warnings.push_back(RTR("The CollisionShape2D node has limited editing options for polygon-based shapes. Consider using a CollisionPolygon2D node instead."));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
|
|
@ -232,7 +227,16 @@ real_t CollisionShape2D::get_one_way_collision_margin() const {
|
|||
return one_way_collision_margin;
|
||||
}
|
||||
|
||||
Color CollisionShape2D::_get_default_debug_color() const {
|
||||
const SceneTree *st = SceneTree::get_singleton();
|
||||
return st ? st->get_debug_collisions_color() : Color(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void CollisionShape2D::set_debug_color(const Color &p_color) {
|
||||
if (debug_color == p_color) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_color = p_color;
|
||||
queue_redraw();
|
||||
}
|
||||
|
|
@ -241,6 +245,8 @@ Color CollisionShape2D::get_debug_color() const {
|
|||
return debug_color;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
bool CollisionShape2D::_property_can_revert(const StringName &p_name) const {
|
||||
if (p_name == "debug_color") {
|
||||
return true;
|
||||
|
|
@ -266,6 +272,8 @@ void CollisionShape2D::_validate_property(PropertyInfo &p_property) const {
|
|||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void CollisionShape2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape2D::set_shape);
|
||||
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape2D::get_shape);
|
||||
|
|
@ -275,16 +283,18 @@ void CollisionShape2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_one_way_collision_margin", "margin"), &CollisionShape2D::set_one_way_collision_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_one_way_collision_margin"), &CollisionShape2D::get_one_way_collision_margin);
|
||||
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape2D::set_debug_color);
|
||||
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape2D::get_debug_color);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "one_way_collision_margin", PROPERTY_HINT_RANGE, "0,128,0.1,suffix:px"), "set_one_way_collision_margin", "get_one_way_collision_margin");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape2D::set_debug_color);
|
||||
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape2D::get_debug_color);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color");
|
||||
// Default value depends on a project setting, override for doc generation purposes.
|
||||
ADD_PROPERTY_DEFAULT("debug_color", Color());
|
||||
ADD_PROPERTY_DEFAULT("debug_color", Color(0.0, 0.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
CollisionShape2D::CollisionShape2D() {
|
||||
|
|
|
|||
|
|
@ -45,25 +45,32 @@ class CollisionShape2D : public Node2D {
|
|||
bool disabled = false;
|
||||
bool one_way_collision = false;
|
||||
real_t one_way_collision_margin = 1.0;
|
||||
Color debug_color;
|
||||
|
||||
void _shape_changed();
|
||||
void _update_in_shape_owner(bool p_xform_only = false);
|
||||
|
||||
// Not wrapped in `#ifdef DEBUG_ENABLED` as it is used for rendering.
|
||||
Color debug_color = Color(0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
Color _get_default_debug_color() const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool _property_can_revert(const StringName &p_name) const;
|
||||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
#else
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
|
||||
#endif // TOOLS_ENABLED
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_shape(const Ref<Shape2D> &p_shape);
|
||||
Ref<Shape2D> get_shape() const;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "kinematic_collision_2d.h"
|
||||
|
||||
#include "scene/2d/physics/character_body_2d.h"
|
||||
#include "scene/2d/physics/physics_body_2d.h"
|
||||
|
||||
Vector2 KinematicCollision2D::get_position() const {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ void PhysicalBone2D::_find_joint_child() {
|
|||
}
|
||||
|
||||
PackedStringArray PhysicalBone2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = RigidBody2D::get_configuration_warnings();
|
||||
|
||||
if (!parent_skeleton) {
|
||||
warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
|
||||
|
|
|
|||
|
|
@ -126,8 +126,7 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion
|
|||
PhysicsServer2D::MotionResult *r = nullptr;
|
||||
PhysicsServer2D::MotionResult temp_result;
|
||||
if (r_collision.is_valid()) {
|
||||
// Needs const_cast because method bindings don't support non-const Ref.
|
||||
r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result);
|
||||
r = &r_collision->result;
|
||||
} else {
|
||||
r = &temp_result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef PHYSICS_BODY_2D_H
|
||||
#define PHYSICS_BODY_2D_H
|
||||
|
||||
#include "core/templates/vset.h"
|
||||
#include "scene/2d/physics/collision_object_2d.h"
|
||||
#include "scene/2d/physics/kinematic_collision_2d.h"
|
||||
#include "scene/resources/physics_material.h"
|
||||
|
|
|
|||
|
|
@ -641,7 +641,7 @@ void RigidBody2D::_notification(int p_what) {
|
|||
PackedStringArray RigidBody2D::get_configuration_warnings() const {
|
||||
Transform2D t = get_transform();
|
||||
|
||||
PackedStringArray warnings = CollisionObject2D::get_configuration_warnings();
|
||||
PackedStringArray warnings = PhysicsBody2D::get_configuration_warnings();
|
||||
|
||||
if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) {
|
||||
warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@
|
|||
#ifndef RIGID_BODY_2D_H
|
||||
#define RIGID_BODY_2D_H
|
||||
|
||||
#include "scene/2d/physics/static_body_2d.h"
|
||||
#include "core/templates/vset.h"
|
||||
#include "scene/2d/physics/physics_body_2d.h"
|
||||
|
||||
class RigidBody2D : public PhysicsBody2D {
|
||||
GDCLASS(RigidBody2D, PhysicsBody2D);
|
||||
|
|
|
|||
|
|
@ -32,9 +32,8 @@
|
|||
|
||||
#include "core/config/engine.h"
|
||||
#include "scene/2d/physics/collision_object_2d.h"
|
||||
#include "scene/2d/physics/physics_body_2d.h"
|
||||
#include "scene/resources/2d/circle_shape_2d.h"
|
||||
#include "servers/physics_2d/godot_physics_server_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
void ShapeCast2D::set_target_position(const Vector2 &p_point) {
|
||||
target_position = p_point;
|
||||
|
|
@ -382,7 +381,7 @@ bool ShapeCast2D::is_collide_with_bodies_enabled() const {
|
|||
return collide_with_bodies;
|
||||
}
|
||||
|
||||
Array ShapeCast2D::_get_collision_result() const {
|
||||
Array ShapeCast2D::get_collision_result() const {
|
||||
Array ret;
|
||||
|
||||
for (int i = 0; i < result.size(); ++i) {
|
||||
|
|
@ -464,7 +463,7 @@ void ShapeCast2D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast2D::set_collide_with_bodies);
|
||||
ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast2D::is_collide_with_bodies_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast2D::_get_collision_result);
|
||||
ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast2D::get_collision_result);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
|
||||
|
|
@ -473,7 +472,7 @@ void ShapeCast2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:px"), "set_margin", "get_margin");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result");
|
||||
ADD_GROUP("Collide With", "collide_with");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#include "scene/2d/node_2d.h"
|
||||
#include "scene/resources/2d/shape_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class CollisionObject2D;
|
||||
|
||||
|
|
@ -60,7 +60,6 @@ class ShapeCast2D : public Node2D {
|
|||
real_t collision_safe_fraction = 1.0;
|
||||
real_t collision_unsafe_fraction = 1.0;
|
||||
|
||||
Array _get_collision_result() const;
|
||||
void _shape_changed();
|
||||
|
||||
protected:
|
||||
|
|
@ -102,6 +101,7 @@ public:
|
|||
void force_shapecast_update();
|
||||
bool is_colliding() const;
|
||||
|
||||
Array get_collision_result() const;
|
||||
int get_collision_count() const;
|
||||
Object *get_collider(int p_idx) const;
|
||||
RID get_collider_rid(int p_idx) const;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,18 @@
|
|||
|
||||
#include "static_body_2d.h"
|
||||
|
||||
#include "scene/resources/2d/capsule_shape_2d.h"
|
||||
#include "scene/resources/2d/circle_shape_2d.h"
|
||||
#include "scene/resources/2d/concave_polygon_shape_2d.h"
|
||||
#include "scene/resources/2d/convex_polygon_shape_2d.h"
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.h"
|
||||
#include "scene/resources/2d/rectangle_shape_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
Callable StaticBody2D::_navmesh_source_geometry_parsing_callback;
|
||||
RID StaticBody2D::_navmesh_source_geometry_parser;
|
||||
|
||||
void StaticBody2D::set_constant_linear_velocity(const Vector2 &p_vel) {
|
||||
constant_linear_velocity = p_vel;
|
||||
|
||||
|
|
@ -77,6 +89,131 @@ void StaticBody2D::_reload_physics_characteristics() {
|
|||
}
|
||||
}
|
||||
|
||||
void StaticBody2D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&StaticBody2D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticBody2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
StaticBody2D *static_body = Object::cast_to<StaticBody2D>(p_node);
|
||||
|
||||
if (static_body == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
if (!(parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
|
||||
if (!(static_body->get_collision_layer() & parsed_collision_mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<uint32_t> shape_owners;
|
||||
static_body->get_shape_owners(&shape_owners);
|
||||
|
||||
for (uint32_t shape_owner : shape_owners) {
|
||||
if (static_body->is_shape_owner_disabled(shape_owner)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
|
||||
|
||||
for (int shape_index = 0; shape_index < shape_count; shape_index++) {
|
||||
Ref<Shape2D> s = static_body->shape_owner_get_shape(shape_owner, shape_index);
|
||||
|
||||
if (s.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Transform2D static_body_xform = p_source_geometry_data->root_node_transform * static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
|
||||
|
||||
RectangleShape2D *rectangle_shape = Object::cast_to<RectangleShape2D>(*s);
|
||||
if (rectangle_shape) {
|
||||
Vector<Vector2> shape_outline;
|
||||
|
||||
const Vector2 &rectangle_size = rectangle_shape->get_size();
|
||||
|
||||
shape_outline.resize(5);
|
||||
shape_outline.write[0] = static_body_xform.xform(-rectangle_size * 0.5);
|
||||
shape_outline.write[1] = static_body_xform.xform(Vector2(rectangle_size.x, -rectangle_size.y) * 0.5);
|
||||
shape_outline.write[2] = static_body_xform.xform(rectangle_size * 0.5);
|
||||
shape_outline.write[3] = static_body_xform.xform(Vector2(-rectangle_size.x, rectangle_size.y) * 0.5);
|
||||
shape_outline.write[4] = static_body_xform.xform(-rectangle_size * 0.5);
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
|
||||
CapsuleShape2D *capsule_shape = Object::cast_to<CapsuleShape2D>(*s);
|
||||
if (capsule_shape) {
|
||||
const real_t capsule_height = capsule_shape->get_height();
|
||||
const real_t capsule_radius = capsule_shape->get_radius();
|
||||
|
||||
Vector<Vector2> shape_outline;
|
||||
const real_t turn_step = Math_TAU / 12.0;
|
||||
shape_outline.resize(14);
|
||||
int shape_outline_inx = 0;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
Vector2 ofs = Vector2(0, (i > 3 && i <= 9) ? -capsule_height * 0.5 + capsule_radius : capsule_height * 0.5 - capsule_radius);
|
||||
|
||||
shape_outline.write[shape_outline_inx] = static_body_xform.xform(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * capsule_radius + ofs);
|
||||
shape_outline_inx += 1;
|
||||
if (i == 3 || i == 9) {
|
||||
shape_outline.write[shape_outline_inx] = static_body_xform.xform(Vector2(Math::sin(i * turn_step), Math::cos(i * turn_step)) * capsule_radius - ofs);
|
||||
shape_outline_inx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
|
||||
CircleShape2D *circle_shape = Object::cast_to<CircleShape2D>(*s);
|
||||
if (circle_shape) {
|
||||
const real_t circle_radius = circle_shape->get_radius();
|
||||
|
||||
Vector<Vector2> shape_outline;
|
||||
int circle_edge_count = 12;
|
||||
shape_outline.resize(circle_edge_count);
|
||||
|
||||
const real_t turn_step = Math_TAU / real_t(circle_edge_count);
|
||||
for (int i = 0; i < circle_edge_count; i++) {
|
||||
shape_outline.write[i] = static_body_xform.xform(Vector2(Math::cos(i * turn_step), Math::sin(i * turn_step)) * circle_radius);
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
|
||||
ConcavePolygonShape2D *concave_polygon_shape = Object::cast_to<ConcavePolygonShape2D>(*s);
|
||||
if (concave_polygon_shape) {
|
||||
Vector<Vector2> shape_outline = concave_polygon_shape->get_segments();
|
||||
|
||||
for (int i = 0; i < shape_outline.size(); i++) {
|
||||
shape_outline.write[i] = static_body_xform.xform(shape_outline[i]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
|
||||
ConvexPolygonShape2D *convex_polygon_shape = Object::cast_to<ConvexPolygonShape2D>(*s);
|
||||
if (convex_polygon_shape) {
|
||||
Vector<Vector2> shape_outline = convex_polygon_shape->get_points();
|
||||
|
||||
for (int i = 0; i < shape_outline.size(); i++) {
|
||||
shape_outline.write[i] = static_body_xform.xform(shape_outline[i]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticBody2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody2D::set_constant_linear_velocity);
|
||||
ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "scene/2d/physics/physics_body_2d.h"
|
||||
|
||||
class NavigationPolygon;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
|
||||
class StaticBody2D : public PhysicsBody2D {
|
||||
GDCLASS(StaticBody2D, PhysicsBody2D);
|
||||
|
||||
|
|
@ -57,6 +60,14 @@ public:
|
|||
|
||||
StaticBody2D(PhysicsServer2D::BodyMode p_mode = PhysicsServer2D::BODY_MODE_STATIC);
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
private:
|
||||
void _reload_physics_characteristics();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,8 +31,14 @@
|
|||
#include "polygon_2d.h"
|
||||
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/2d/navigation_polygon.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
#include "skeleton_2d.h"
|
||||
|
||||
Callable Polygon2D::_navmesh_source_geometry_parsing_callback;
|
||||
RID Polygon2D::_navmesh_source_geometry_parser;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Dictionary Polygon2D::_edit_get_state() const {
|
||||
Dictionary state = Node2D::_edit_get_state();
|
||||
|
|
@ -57,7 +63,9 @@ Point2 Polygon2D::_edit_get_pivot() const {
|
|||
bool Polygon2D::_edit_use_pivot() const {
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 Polygon2D::_edit_get_rect() const {
|
||||
if (rect_cache_dirty) {
|
||||
int l = polygon.size();
|
||||
|
|
@ -88,7 +96,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler
|
|||
}
|
||||
return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d);
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void Polygon2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (!invert && p_property.name == "invert_border") {
|
||||
|
|
@ -602,6 +610,36 @@ NodePath Polygon2D::get_skeleton() const {
|
|||
return skeleton;
|
||||
}
|
||||
|
||||
void Polygon2D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&Polygon2D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon2D::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
Polygon2D *polygon_2d = Object::cast_to<Polygon2D>(p_node);
|
||||
|
||||
if (polygon_2d == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
|
||||
if (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_MESH_INSTANCES || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) {
|
||||
const Transform2D polygon_2d_xform = p_source_geometry_data->root_node_transform * polygon_2d->get_global_transform();
|
||||
|
||||
Vector<Vector2> shape_outline = polygon_2d->get_polygon();
|
||||
for (int i = 0; i < shape_outline.size(); i++) {
|
||||
shape_outline.write[i] = polygon_2d_xform.xform(shape_outline[i]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_obstruction_outline(shape_outline);
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon);
|
||||
ClassDB::bind_method(D_METHOD("get_polygon"), &Polygon2D::get_polygon);
|
||||
|
|
@ -682,7 +720,7 @@ void Polygon2D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_bones", "_get_bones");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class NavigationPolygon;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
|
||||
class Polygon2D : public Node2D {
|
||||
GDCLASS(Polygon2D, Node2D);
|
||||
|
||||
|
|
@ -87,11 +90,14 @@ public:
|
|||
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
|
||||
virtual Point2 _edit_get_pivot() const override;
|
||||
virtual bool _edit_use_pivot() const override;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_polygon(const Vector<Vector2> &p_polygon);
|
||||
Vector<Vector2> get_polygon() const;
|
||||
|
|
@ -147,6 +153,14 @@ public:
|
|||
void set_skeleton(const NodePath &p_skeleton);
|
||||
NodePath get_skeleton() const;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
Polygon2D();
|
||||
~Polygon2D();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -114,6 +114,16 @@ void RemoteTransform2D::_notification(int p_what) {
|
|||
_update_cache();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESET_PHYSICS_INTERPOLATION: {
|
||||
if (cache.is_valid()) {
|
||||
_update_remote();
|
||||
Node2D *n = Object::cast_to<Node2D>(ObjectDB::get_instance(cache));
|
||||
if (n) {
|
||||
n->reset_physics_interpolation();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED:
|
||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||
if (!is_inside_tree()) {
|
||||
|
|
@ -201,7 +211,7 @@ void RemoteTransform2D::force_update_cache() {
|
|||
}
|
||||
|
||||
PackedStringArray RemoteTransform2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
|
||||
warnings.push_back(RTR("Path property must point to a valid Node2D node to work."));
|
||||
|
|
|
|||
|
|
@ -39,19 +39,17 @@
|
|||
#endif //TOOLS_ENABLED
|
||||
|
||||
bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("auto_calculate_length_and_angle")) {
|
||||
if (p_path == SNAME("auto_calculate_length_and_angle")) {
|
||||
set_autocalculate_length_and_angle(p_value);
|
||||
} else if (path.begins_with("length")) {
|
||||
} else if (p_path == SNAME("length")) {
|
||||
set_length(p_value);
|
||||
} else if (path.begins_with("bone_angle")) {
|
||||
} else if (p_path == SNAME("bone_angle")) {
|
||||
set_bone_angle(Math::deg_to_rad(real_t(p_value)));
|
||||
} else if (path.begins_with("default_length")) {
|
||||
} else if (p_path == SNAME("default_length")) {
|
||||
set_length(p_value);
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor_settings/show_bone_gizmo")) {
|
||||
else if (p_path == SNAME("editor_settings/show_bone_gizmo")) {
|
||||
_editor_set_show_bone_gizmo(p_value);
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
|
@ -63,19 +61,17 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
|
|||
}
|
||||
|
||||
bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("auto_calculate_length_and_angle")) {
|
||||
if (p_path == SNAME("auto_calculate_length_and_angle")) {
|
||||
r_ret = get_autocalculate_length_and_angle();
|
||||
} else if (path.begins_with("length")) {
|
||||
} else if (p_path == SNAME("length")) {
|
||||
r_ret = get_length();
|
||||
} else if (path.begins_with("bone_angle")) {
|
||||
} else if (p_path == SNAME("bone_angle")) {
|
||||
r_ret = Math::rad_to_deg(get_bone_angle());
|
||||
} else if (path.begins_with("default_length")) {
|
||||
} else if (p_path == SNAME("default_length")) {
|
||||
r_ret = get_length();
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
else if (path.begins_with("editor_settings/show_bone_gizmo")) {
|
||||
else if (p_path == SNAME("editor_settings/show_bone_gizmo")) {
|
||||
r_ret = _editor_get_show_bone_gizmo();
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
|
@ -163,7 +159,7 @@ void Bone2D::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (skeleton) {
|
||||
for (int i = 0; i < skeleton->bones.size(); i++) {
|
||||
for (uint32_t i = 0; i < skeleton->bones.size(); i++) {
|
||||
if (skeleton->bones[i].bone == this) {
|
||||
skeleton->bones.remove_at(i);
|
||||
break;
|
||||
|
|
@ -330,9 +326,7 @@ bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p
|
|||
rel = (p_other_bone->get_global_position() - get_global_position());
|
||||
rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
|
||||
} else {
|
||||
real_t angle_to_use = get_rotation() + bone_angle;
|
||||
rel = Vector2(cos(angle_to_use), sin(angle_to_use)) * (length * MIN(get_global_scale().x, get_global_scale().y));
|
||||
rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
|
||||
rel = Vector2(Math::cos(bone_angle), Math::sin(bone_angle)) * length * get_global_scale();
|
||||
}
|
||||
|
||||
Vector2 relt = rel.rotated(Math_PI * 0.5).normalized() * bone_width;
|
||||
|
|
@ -418,7 +412,7 @@ int Bone2D::get_index_in_skeleton() const {
|
|||
}
|
||||
|
||||
PackedStringArray Bone2D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
if (!skeleton) {
|
||||
if (parent_bone) {
|
||||
warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node."));
|
||||
|
|
@ -518,23 +512,19 @@ Bone2D::~Bone2D() {
|
|||
//////////////////////////////////////
|
||||
|
||||
bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("modification_stack")) {
|
||||
if (p_path == SNAME("modification_stack")) {
|
||||
set_modification_stack(p_value);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const {
|
||||
String path = p_path;
|
||||
|
||||
if (path.begins_with("modification_stack")) {
|
||||
if (p_path == SNAME("modification_stack")) {
|
||||
r_ret = get_modification_stack();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
|
|
@ -565,17 +555,17 @@ void Skeleton2D::_update_bone_setup() {
|
|||
|
||||
bones.sort(); //sorting so that they are always in the same order/index
|
||||
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
|
||||
bones.write[i].bone->skeleton_index = i;
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
|
||||
bones[i].bone->skeleton_index = i;
|
||||
Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
|
||||
if (parent_bone) {
|
||||
bones.write[i].parent_index = parent_bone->skeleton_index;
|
||||
bones[i].parent_index = parent_bone->skeleton_index;
|
||||
} else {
|
||||
bones.write[i].parent_index = -1;
|
||||
bones[i].parent_index = -1;
|
||||
}
|
||||
|
||||
bones.write[i].local_pose_override = bones[i].bone->get_skeleton_rest();
|
||||
bones[i].local_pose_override = bones[i].bone->get_skeleton_rest();
|
||||
}
|
||||
|
||||
transform_dirty = true;
|
||||
|
|
@ -604,16 +594,16 @@ void Skeleton2D::_update_transform() {
|
|||
|
||||
transform_dirty = false;
|
||||
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
ERR_CONTINUE(bones[i].parent_index >= i);
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
ERR_CONTINUE(bones[i].parent_index >= (int)i);
|
||||
if (bones[i].parent_index >= 0) {
|
||||
bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
|
||||
bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
|
||||
} else {
|
||||
bones.write[i].accum_transform = bones[i].bone->get_transform();
|
||||
bones[i].accum_transform = bones[i].bone->get_transform();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
|
||||
RS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
|
||||
}
|
||||
|
|
@ -623,6 +613,7 @@ int Skeleton2D::get_bone_count() const {
|
|||
ERR_FAIL_COND_V(!is_inside_tree(), 0);
|
||||
|
||||
if (bone_setup_dirty) {
|
||||
// TODO: Is this necessary? It doesn't seem to change bones.size()
|
||||
const_cast<Skeleton2D *>(this)->_update_bone_setup();
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +622,7 @@ int Skeleton2D::get_bone_count() const {
|
|||
|
||||
Bone2D *Skeleton2D::get_bone(int p_idx) {
|
||||
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
|
||||
ERR_FAIL_INDEX_V(p_idx, bones.size(), nullptr);
|
||||
ERR_FAIL_INDEX_V(p_idx, (int)bones.size(), nullptr);
|
||||
|
||||
return bones[p_idx].bone;
|
||||
}
|
||||
|
|
@ -743,14 +734,14 @@ RID Skeleton2D::get_skeleton() const {
|
|||
}
|
||||
|
||||
void Skeleton2D::set_bone_local_pose_override(int p_bone_idx, Transform2D p_override, real_t p_amount, bool p_persistent) {
|
||||
ERR_FAIL_INDEX_MSG(p_bone_idx, bones.size(), "Bone index is out of range!");
|
||||
bones.write[p_bone_idx].local_pose_override = p_override;
|
||||
bones.write[p_bone_idx].local_pose_override_amount = p_amount;
|
||||
bones.write[p_bone_idx].local_pose_override_persistent = p_persistent;
|
||||
ERR_FAIL_INDEX_MSG(p_bone_idx, (int)bones.size(), "Bone index is out of range!");
|
||||
bones[p_bone_idx].local_pose_override = p_override;
|
||||
bones[p_bone_idx].local_pose_override_amount = p_amount;
|
||||
bones[p_bone_idx].local_pose_override_persistent = p_persistent;
|
||||
}
|
||||
|
||||
Transform2D Skeleton2D::get_bone_local_pose_override(int p_bone_idx) {
|
||||
ERR_FAIL_INDEX_V_MSG(p_bone_idx, bones.size(), Transform2D(), "Bone index is out of range!");
|
||||
ERR_FAIL_INDEX_V_MSG(p_bone_idx, (int)bones.size(), Transform2D(), "Bone index is out of range!");
|
||||
return bones[p_bone_idx].local_pose_override;
|
||||
}
|
||||
|
||||
|
|
@ -776,12 +767,12 @@ Ref<SkeletonModificationStack2D> Skeleton2D::get_modification_stack() const {
|
|||
}
|
||||
|
||||
void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
|
||||
if (!modification_stack.is_valid()) {
|
||||
if (modification_stack.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not cache the transform changes caused by the modifications!
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
bones[i].bone->copy_transform_to_cache = false;
|
||||
}
|
||||
|
||||
|
|
@ -793,7 +784,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
|
|||
|
||||
// Only apply the local pose override on _process. Otherwise, just calculate the local_pose_override and reset the transform.
|
||||
if (p_execution_mode == SkeletonModificationStack2D::EXECUTION_MODE::execution_mode_process) {
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
if (bones[i].local_pose_override_amount > 0) {
|
||||
bones[i].bone->set_meta("_local_pose_override_enabled_", true);
|
||||
|
||||
|
|
@ -803,7 +794,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
|
|||
bones[i].bone->propagate_call("force_update_transform");
|
||||
|
||||
if (bones[i].local_pose_override_persistent) {
|
||||
bones.write[i].local_pose_override_amount = 0.0;
|
||||
bones[i].local_pose_override_amount = 0.0;
|
||||
}
|
||||
} else {
|
||||
// TODO: see if there is a way to undo the override without having to resort to setting every bone's transform.
|
||||
|
|
@ -814,7 +805,7 @@ void Skeleton2D::execute_modifications(real_t p_delta, int p_execution_mode) {
|
|||
}
|
||||
|
||||
// Cache any future transform changes
|
||||
for (int i = 0; i < bones.size(); i++) {
|
||||
for (uint32_t i = 0; i < bones.size(); i++) {
|
||||
bones[i].bone->copy_transform_to_cache = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class Skeleton2D : public Node2D {
|
|||
bool local_pose_override_persistent = false;
|
||||
};
|
||||
|
||||
Vector<Bone> bones;
|
||||
LocalVector<Bone> bones;
|
||||
|
||||
bool bone_setup_dirty = true;
|
||||
void _make_bone_setup_dirty();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "sprite_2d.h"
|
||||
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Dictionary Sprite2D::_edit_get_state() const {
|
||||
|
|
@ -56,7 +56,9 @@ Point2 Sprite2D::_edit_get_pivot() const {
|
|||
bool Sprite2D::_edit_use_pivot() const {
|
||||
return true;
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool Sprite2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
|
||||
return is_pixel_opaque(p_point);
|
||||
}
|
||||
|
|
@ -68,7 +70,7 @@ Rect2 Sprite2D::_edit_get_rect() const {
|
|||
bool Sprite2D::_edit_use_rect() const {
|
||||
return texture.is_valid();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Rect2 Sprite2D::get_anchorable_rect() const {
|
||||
return get_rect();
|
||||
|
|
@ -421,7 +423,7 @@ void Sprite2D::_validate_property(PropertyInfo &p_property) const {
|
|||
p_property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
|
||||
}
|
||||
|
||||
if (!region_enabled && (p_property.name == "region_rect" || p_property.name == "region_filter_clip")) {
|
||||
if (!region_enabled && (p_property.name == "region_rect" || p_property.name == "region_filter_clip_enabled")) {
|
||||
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,11 +74,14 @@ public:
|
|||
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
|
||||
virtual Point2 _edit_get_pivot() const override;
|
||||
virtual bool _edit_use_pivot() const override;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
|
||||
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
bool is_pixel_opaque(const Point2 &p_point) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@
|
|||
#include "tile_map.compat.inc"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
#define TILEMAP_CALL_FOR_LAYER(layer, function, ...) \
|
||||
if (layer < 0) { \
|
||||
|
|
@ -48,6 +49,9 @@
|
|||
ERR_FAIL_INDEX_V(layer, (int)layers.size(), err_value); \
|
||||
return layers[layer]->function(__VA_ARGS__);
|
||||
|
||||
Callable TileMap::_navmesh_source_geometry_parsing_callback;
|
||||
RID TileMap::_navmesh_source_geometry_parser;
|
||||
|
||||
void TileMap::_tile_set_changed() {
|
||||
update_configuration_warnings();
|
||||
}
|
||||
|
|
@ -75,7 +79,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap
|
|||
for (int i = 0; i < c; i += offset) {
|
||||
const uint8_t *ptr = (const uint8_t *)&r[i];
|
||||
uint8_t local[12];
|
||||
for (int j = 0; j < ((p_format >= TileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 12 : 8); j++) {
|
||||
const int buffer_size = (p_format >= TILE_MAP_DATA_FORMAT_2) ? 12 : 8;
|
||||
for (int j = 0; j < buffer_size; j++) {
|
||||
local[j] = ptr[j];
|
||||
}
|
||||
|
||||
|
|
@ -89,8 +94,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap
|
|||
SWAP(local[8], local[11]);
|
||||
SWAP(local[9], local[10]);
|
||||
}
|
||||
#endif
|
||||
// Extracts position in TileMap.
|
||||
#endif // BIG_ENDIAN_ENABLED
|
||||
// Extracts position in TileMap.
|
||||
int16_t x = decode_uint16(&local[0]);
|
||||
int16_t y = decode_uint16(&local[2]);
|
||||
|
||||
|
|
@ -172,7 +177,7 @@ void TileMap::_notification(int p_what) {
|
|||
bool in_editor = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
in_editor = Engine::get_singleton()->is_editor_hint();
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
if (is_inside_tree() && collision_animatable && !in_editor) {
|
||||
// Update transform on the physics tick when in animatable mode.
|
||||
last_valid_transform = new_transform;
|
||||
|
|
@ -188,7 +193,7 @@ void TileMap::_notification(int p_what) {
|
|||
bool in_editor = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
in_editor = Engine::get_singleton()->is_editor_hint();
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
if (is_inside_tree() && collision_animatable && !in_editor) {
|
||||
// Store last valid transform.
|
||||
|
|
@ -209,7 +214,7 @@ void TileMap::force_update(int p_layer) {
|
|||
notify_runtime_tile_data_update(p_layer);
|
||||
update_internals();
|
||||
}
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
void TileMap::set_rendering_quadrant_size(int p_size) {
|
||||
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
|
||||
|
|
@ -532,12 +537,24 @@ TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, boo
|
|||
}
|
||||
}
|
||||
|
||||
bool TileMap::is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
|
||||
return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
||||
}
|
||||
|
||||
bool TileMap::is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
|
||||
return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
||||
}
|
||||
|
||||
bool TileMap::is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
|
||||
return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
||||
}
|
||||
|
||||
Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
|
||||
TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array);
|
||||
}
|
||||
|
||||
Vector2i TileMap::map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern) {
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), Vector2i());
|
||||
return tile_set->map_pattern(p_position_in_tilemap, p_coords_in_pattern, p_pattern);
|
||||
}
|
||||
|
||||
|
|
@ -637,7 +654,7 @@ void TileMap::notify_runtime_tile_data_update(int p_layer) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 TileMap::_edit_get_rect() const {
|
||||
// Return the visible rect of the tilemap.
|
||||
if (layers.is_empty()) {
|
||||
|
|
@ -655,7 +672,7 @@ Rect2 TileMap::_edit_get_rect() const {
|
|||
const_cast<TileMap *>(this)->item_rect_changed(any_changed);
|
||||
return rect;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
|
||||
int index;
|
||||
|
|
@ -714,7 +731,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
r_ret = get_rendering_quadrant_size();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
else {
|
||||
return property_helper.property_get_value(sname, r_ret);
|
||||
}
|
||||
|
|
@ -726,22 +743,22 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
|
|||
}
|
||||
|
||||
Vector2 TileMap::map_to_local(const Vector2i &p_pos) const {
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2());
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), Vector2());
|
||||
return tile_set->map_to_local(p_pos);
|
||||
}
|
||||
|
||||
Vector2i TileMap::local_to_map(const Vector2 &p_pos) const {
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), Vector2i());
|
||||
return tile_set->local_to_map(p_pos);
|
||||
}
|
||||
|
||||
bool TileMap::is_existing_neighbor(TileSet::CellNeighbor p_cell_neighbor) const {
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), false);
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), false);
|
||||
return tile_set->is_existing_neighbor(p_cell_neighbor);
|
||||
}
|
||||
|
||||
Vector2i TileMap::get_neighbor_cell(const Vector2i &p_coords, TileSet::CellNeighbor p_cell_neighbor) const {
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), Vector2i());
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), Vector2i());
|
||||
return tile_set->get_neighbor_cell(p_coords, p_cell_neighbor);
|
||||
}
|
||||
|
||||
|
|
@ -807,7 +824,7 @@ void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) {
|
|||
}
|
||||
|
||||
TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) {
|
||||
if (!tile_set.is_valid()) {
|
||||
if (tile_set.is_null()) {
|
||||
return TypedArray<Vector2i>();
|
||||
}
|
||||
|
||||
|
|
@ -815,7 +832,7 @@ TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) {
|
|||
}
|
||||
|
||||
PackedStringArray TileMap::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node2D::get_configuration_warnings();
|
||||
|
||||
warnings.push_back(RTR("The TileMap node is deprecated as it is superseded by the use of multiple TileMapLayer nodes.\nTo convert a TileMap to a set of TileMapLayer nodes, open the TileMap bottom panel with this node selected, click the toolbox icon in the top-right corner and choose \"Extract TileMap layers as individual TileMapLayer nodes\"."));
|
||||
|
||||
|
|
@ -926,6 +943,10 @@ void TileMap::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_h, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_v, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("is_cell_transposed", "layer", "coords", "use_proxies"), &TileMap::is_cell_transposed, DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid);
|
||||
ClassDB::bind_method(D_METHOD("get_layer_for_body_rid", "body"), &TileMap::get_layer_for_body_rid);
|
||||
|
||||
|
|
@ -1006,5 +1027,33 @@ TileMap::TileMap() {
|
|||
property_helper.setup_for_instance(base_property_helper, this);
|
||||
}
|
||||
|
||||
void TileMap::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&TileMap::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMap::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
TileMap *nb_tilemap = Object::cast_to<TileMap>(p_node);
|
||||
|
||||
if (nb_tilemap == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false.
|
||||
bool recurse_children = p_navigation_mesh->get_source_geometry_mode() != NavigationPolygon::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
|
||||
if (!recurse_children) {
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i));
|
||||
if (tile_map_layer && tile_map_layer->get_index_in_tile_map() >= 0) {
|
||||
tile_map_layer->navmesh_parse_source_geometry(p_navigation_mesh, p_source_geometry_data, tile_map_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef TILEMAP_CALL_FOR_LAYER
|
||||
#undef TILEMAP_CALL_FOR_LAYER_V
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class Control;
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
class TileMapLayer;
|
||||
class TerrainConstraint;
|
||||
|
||||
|
|
@ -107,16 +108,16 @@ protected:
|
|||
VisibilityMode _get_navigation_visibility_mode_bind_compat_87115();
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void force_update(int p_layer);
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
void set_rendering_quadrant_size(int p_size);
|
||||
int get_rendering_quadrant_size() const;
|
||||
|
|
@ -167,6 +168,10 @@ public:
|
|||
// Helper method to make accessing the data easier.
|
||||
TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
|
||||
bool is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
bool is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
bool is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
|
||||
|
||||
// Patterns.
|
||||
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
|
||||
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
|
||||
|
|
@ -234,6 +239,14 @@ public:
|
|||
// Configuration warnings.
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
TileMap();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@
|
|||
#include "core/io/marshalls.h"
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
#include "servers/navigation_server_2d.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#include "servers/navigation_server_3d.h"
|
||||
#endif // DEBUG_ENABLED
|
||||
Callable TileMapLayer::_navmesh_source_geometry_parsing_callback;
|
||||
RID TileMapLayer::_navmesh_source_geometry_parser;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
/////////////////////////////// Debug //////////////////////////////////////////
|
||||
|
|
@ -313,7 +313,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
|
|||
rs->canvas_item_set_material(ci, mat->get_rid());
|
||||
}
|
||||
rs->canvas_item_set_parent(ci, get_canvas_item());
|
||||
rs->canvas_item_set_use_parent_material(ci, !mat.is_valid());
|
||||
rs->canvas_item_set_use_parent_material(ci, mat.is_null());
|
||||
|
||||
Transform2D xform(0, rendering_quadrant->canvas_items_position);
|
||||
rs->canvas_item_set_transform(ci, xform);
|
||||
|
|
@ -411,7 +411,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
|
|||
}
|
||||
|
||||
// ----------- Occluders processing -----------
|
||||
if (forced_cleanup) {
|
||||
if (forced_cleanup || !occlusion_enabled) {
|
||||
// Clean everything.
|
||||
for (KeyValue<Vector2i, CellData> &kv : tile_map_layer_data) {
|
||||
_rendering_occluders_clear_cell(kv.value);
|
||||
|
|
@ -433,7 +433,7 @@ void TileMapLayer::_rendering_update(bool p_force_cleanup) {
|
|||
|
||||
// -----------
|
||||
// Mark the rendering state as up to date.
|
||||
_rendering_was_cleaned_up = forced_cleanup;
|
||||
_rendering_was_cleaned_up = forced_cleanup || !occlusion_enabled;
|
||||
}
|
||||
|
||||
void TileMapLayer::_rendering_notification(int p_what) {
|
||||
|
|
@ -443,13 +443,15 @@ void TileMapLayer::_rendering_notification(int p_what) {
|
|||
Transform2D tilemap_xform = get_global_transform();
|
||||
for (KeyValue<Vector2i, CellData> &kv : tile_map_layer_data) {
|
||||
const CellData &cell_data = kv.value;
|
||||
for (const RID &occluder : cell_data.occluders) {
|
||||
if (occluder.is_null()) {
|
||||
continue;
|
||||
for (const LocalVector<RID> &polygons : cell_data.occluders) {
|
||||
for (const RID &rid : polygons) {
|
||||
if (rid.is_null()) {
|
||||
continue;
|
||||
}
|
||||
Transform2D xform(0, tile_set->map_to_local(kv.key));
|
||||
rs->canvas_light_occluder_attach_to_canvas(rid, get_canvas());
|
||||
rs->canvas_light_occluder_set_transform(rid, tilemap_xform * xform);
|
||||
}
|
||||
Transform2D xform(0, tile_set->map_to_local(kv.key));
|
||||
rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
|
||||
rs->canvas_light_occluder_set_transform(occluder, tilemap_xform * xform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -557,8 +559,10 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) {
|
|||
RenderingServer *rs = RenderingServer::get_singleton();
|
||||
|
||||
// Free the occluders.
|
||||
for (const RID &rid : r_cell_data.occluders) {
|
||||
rs->free(rid);
|
||||
for (const LocalVector<RID> &polygons : r_cell_data.occluders) {
|
||||
for (const RID &rid : polygons) {
|
||||
rs->free(rid);
|
||||
}
|
||||
}
|
||||
r_cell_data.occluders.clear();
|
||||
}
|
||||
|
|
@ -566,11 +570,12 @@ void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) {
|
|||
void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
|
||||
RenderingServer *rs = RenderingServer::get_singleton();
|
||||
|
||||
// Free unused occluders then resize the occluders array.
|
||||
// Free unused occluders then resize the occluder array.
|
||||
for (uint32_t i = tile_set->get_occlusion_layers_count(); i < r_cell_data.occluders.size(); i++) {
|
||||
RID occluder_id = r_cell_data.occluders[i];
|
||||
if (occluder_id.is_valid()) {
|
||||
rs->free(occluder_id);
|
||||
for (const RID &occluder_id : r_cell_data.occluders[i]) {
|
||||
if (occluder_id.is_valid()) {
|
||||
rs->free(occluder_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
r_cell_data.occluders.resize(tile_set->get_occlusion_layers_count());
|
||||
|
|
@ -598,30 +603,42 @@ void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) {
|
|||
// Create, update or clear occluders.
|
||||
bool needs_set_not_interpolated = is_inside_tree() && get_tree()->is_physics_interpolation_enabled() && !is_physics_interpolated();
|
||||
for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) {
|
||||
Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer_index);
|
||||
LocalVector<RID> &occluders = r_cell_data.occluders[occlusion_layer_index];
|
||||
|
||||
RID &occluder = r_cell_data.occluders[occlusion_layer_index];
|
||||
|
||||
if (occluder_polygon.is_valid()) {
|
||||
// Create or update occluder.
|
||||
Transform2D xform;
|
||||
xform.set_origin(tile_set->map_to_local(r_cell_data.coords));
|
||||
if (!occluder.is_valid()) {
|
||||
occluder = rs->canvas_light_occluder_create();
|
||||
if (needs_set_not_interpolated) {
|
||||
rs->canvas_light_occluder_set_interpolated(occluder, false);
|
||||
}
|
||||
// Free unused occluders then resize the occluders array.
|
||||
for (uint32_t i = tile_data->get_occluder_polygons_count(occlusion_layer_index); i < r_cell_data.occluders[occlusion_layer_index].size(); i++) {
|
||||
RID occluder_id = occluders[i];
|
||||
if (occluder_id.is_valid()) {
|
||||
rs->free(occluder_id);
|
||||
}
|
||||
rs->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform);
|
||||
rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid());
|
||||
rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
|
||||
rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index));
|
||||
rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index));
|
||||
} else {
|
||||
// Clear occluder.
|
||||
if (occluder.is_valid()) {
|
||||
rs->free(occluder);
|
||||
occluder = RID();
|
||||
}
|
||||
occluders.resize(tile_data->get_occluder_polygons_count(occlusion_layer_index));
|
||||
|
||||
for (uint32_t occlusion_polygon_index = 0; occlusion_polygon_index < occluders.size(); occlusion_polygon_index++) {
|
||||
RID &occluder = occluders[occlusion_polygon_index];
|
||||
Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder_polygon(occlusion_layer_index, occlusion_polygon_index);
|
||||
if (occluder_polygon.is_valid()) {
|
||||
// Create or update occluder.
|
||||
|
||||
Transform2D xform;
|
||||
xform.set_origin(tile_set->map_to_local(r_cell_data.coords));
|
||||
if (!occluder.is_valid()) {
|
||||
occluder = rs->canvas_light_occluder_create();
|
||||
if (needs_set_not_interpolated) {
|
||||
rs->canvas_light_occluder_set_interpolated(occluder, false);
|
||||
}
|
||||
}
|
||||
rs->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform);
|
||||
rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder_polygon(occlusion_layer_index, occlusion_polygon_index, flip_h, flip_v, transpose)->get_rid());
|
||||
rs->canvas_light_occluder_attach_to_canvas(occluder, get_canvas());
|
||||
rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index));
|
||||
rs->canvas_light_occluder_set_as_sdf_collision(occluder, tile_set->get_occlusion_layer_sdf_collision(occlusion_layer_index));
|
||||
} else {
|
||||
// Clear occluder.
|
||||
if (occluder.is_valid()) {
|
||||
rs->free(occluder);
|
||||
occluder = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -655,7 +672,7 @@ void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Ve
|
|||
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
|
||||
if (atlas_source) {
|
||||
Vector2i grid_size = atlas_source->get_atlas_grid_size();
|
||||
if (!atlas_source->get_runtime_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
|
||||
if (atlas_source->get_runtime_texture().is_null() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) {
|
||||
// Generate a random color from the hashed values of the tiles.
|
||||
Array to_hash;
|
||||
to_hash.push_back(c.source_id);
|
||||
|
|
@ -806,6 +823,7 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
|
|||
Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
|
||||
uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer);
|
||||
uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer);
|
||||
real_t physics_priority = tile_set->get_physics_layer_collision_priority(tile_set_physics_layer);
|
||||
|
||||
RID body = r_cell_data.bodies[tile_set_physics_layer];
|
||||
if (tile_data->get_collision_polygons_count(tile_set_physics_layer) == 0) {
|
||||
|
|
@ -832,11 +850,12 @@ void TileMapLayer::_physics_update_cell(CellData &r_cell_data) {
|
|||
ps->body_attach_object_instance_id(body, tile_map_node ? tile_map_node->get_instance_id() : get_instance_id());
|
||||
ps->body_set_collision_layer(body, physics_layer);
|
||||
ps->body_set_collision_mask(body, physics_mask);
|
||||
ps->body_set_collision_priority(body, physics_priority);
|
||||
ps->body_set_pickable(body, false);
|
||||
ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer));
|
||||
ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer));
|
||||
|
||||
if (!physics_material.is_valid()) {
|
||||
if (physics_material.is_null()) {
|
||||
ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
|
||||
ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
|
||||
} else {
|
||||
|
|
@ -929,7 +948,7 @@ void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vect
|
|||
rs->canvas_item_add_set_transform(p_canvas_item, Transform2D());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
/////////////////////////////// Navigation //////////////////////////////////////
|
||||
|
|
@ -957,7 +976,7 @@ void TileMapLayer::_navigation_update(bool p_force_cleanup) {
|
|||
// Create a dedicated map for each layer.
|
||||
RID new_layer_map = ns->map_create();
|
||||
// Set the default NavigationPolygon cell_size on the new map as a mismatch causes an error.
|
||||
ns->map_set_cell_size(new_layer_map, 1.0);
|
||||
ns->map_set_cell_size(new_layer_map, NavigationDefaults2D::navmesh_cell_size);
|
||||
ns->map_set_active(new_layer_map, true);
|
||||
navigation_map_override = new_layer_map;
|
||||
}
|
||||
|
|
@ -1313,7 +1332,7 @@ void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vecto
|
|||
|
||||
TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
|
||||
if (scenes_collection_source) {
|
||||
if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) {
|
||||
if (scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_null() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) {
|
||||
// Generate a random color from the hashed values of the tiles.
|
||||
Array to_hash;
|
||||
to_hash.push_back(c.source_id);
|
||||
|
|
@ -1394,7 +1413,7 @@ void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_dat
|
|||
|
||||
tile_map_node->GDVIRTUAL_CALL(_tile_data_runtime_update, layer_index_in_tile_map_node, r_cell_data.coords, tile_data_runtime_use);
|
||||
|
||||
if (p_auto_add_to_dirty_list) {
|
||||
if (p_auto_add_to_dirty_list && !r_cell_data.dirty_list_element.in_list()) {
|
||||
dirty.cell_list.add(&r_cell_data.dirty_list_element);
|
||||
}
|
||||
}
|
||||
|
|
@ -1409,7 +1428,7 @@ void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_dat
|
|||
|
||||
GDVIRTUAL_CALL(_tile_data_runtime_update, r_cell_data.coords, tile_data_runtime_use);
|
||||
|
||||
if (p_auto_add_to_dirty_list) {
|
||||
if (p_auto_add_to_dirty_list && !r_cell_data.dirty_list_element.in_list()) {
|
||||
dirty.cell_list.add(&r_cell_data.dirty_list_element);
|
||||
}
|
||||
}
|
||||
|
|
@ -1441,6 +1460,24 @@ void TileMapLayer::_clear_runtime_update_tile_data_for_cell(CellData &r_cell_dat
|
|||
}
|
||||
}
|
||||
|
||||
void TileMapLayer::_update_cells_callback(bool p_force_cleanup) {
|
||||
if (!GDVIRTUAL_IS_OVERRIDDEN(_update_cells)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we should cleanup everything.
|
||||
bool forced_cleanup = p_force_cleanup || !enabled || tile_set.is_null() || !is_visible_in_tree();
|
||||
|
||||
// List all the dirty cell's positions to notify script of cell updates.
|
||||
TypedArray<Vector2i> dirty_cell_positions;
|
||||
for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) {
|
||||
CellData &cell_data = *cell_data_list_element->self();
|
||||
dirty_cell_positions.push_back(cell_data.coords);
|
||||
}
|
||||
|
||||
GDVIRTUAL_CALL(_update_cells, dirty_cell_positions, forced_cleanup);
|
||||
}
|
||||
|
||||
TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const {
|
||||
if (tile_set.is_null()) {
|
||||
return TileSet::TerrainsPattern();
|
||||
|
|
@ -1654,6 +1691,10 @@ void TileMapLayer::_internal_update(bool p_force_cleanup) {
|
|||
// This may add cells to the dirty list if a runtime modification has been notified.
|
||||
_build_runtime_update_tile_data(p_force_cleanup);
|
||||
|
||||
// Callback for implementing custom subsystems.
|
||||
// This may add to the dirty list if some cells are changed inside _update_cells.
|
||||
_update_cells_callback(p_force_cleanup);
|
||||
|
||||
// Update all subsystems.
|
||||
_rendering_update(p_force_cleanup);
|
||||
_physics_update(p_force_cleanup);
|
||||
|
|
@ -1709,11 +1750,13 @@ void TileMapLayer::_physics_interpolated_changed() {
|
|||
}
|
||||
|
||||
for (const KeyValue<Vector2i, CellData> &E : tile_map_layer_data) {
|
||||
for (const RID &occluder : E.value.occluders) {
|
||||
if (occluder.is_valid()) {
|
||||
rs->canvas_light_occluder_set_interpolated(occluder, interpolated);
|
||||
if (needs_reset) {
|
||||
rs->canvas_light_occluder_reset_physics_interpolation(occluder);
|
||||
for (const LocalVector<RID> &polygons : E.value.occluders) {
|
||||
for (const RID &occluder_id : polygons) {
|
||||
if (occluder_id.is_valid()) {
|
||||
rs->canvas_light_occluder_set_interpolated(occluder_id, interpolated);
|
||||
if (needs_reset) {
|
||||
rs->canvas_light_occluder_reset_physics_interpolation(occluder_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1773,6 +1816,10 @@ void TileMapLayer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapLayer::get_cell_alternative_tile);
|
||||
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &TileMapLayer::get_cell_tile_data);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "coords"), &TileMapLayer::is_cell_flipped_h);
|
||||
ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "coords"), &TileMapLayer::is_cell_flipped_v);
|
||||
ClassDB::bind_method(D_METHOD("is_cell_transposed", "coords"), &TileMapLayer::is_cell_transposed);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapLayer::get_used_cells);
|
||||
ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::get_used_cells_by_id, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
|
||||
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMapLayer::get_used_rect);
|
||||
|
|
@ -1791,7 +1838,7 @@ void TileMapLayer::_bind_methods() {
|
|||
|
||||
// --- Runtime ---
|
||||
ClassDB::bind_method(D_METHOD("update_internals"), &TileMapLayer::update_internals);
|
||||
ClassDB::bind_method(D_METHOD("notify_runtime_tile_data_update"), &TileMapLayer::notify_runtime_tile_data_update, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("notify_runtime_tile_data_update"), &TileMapLayer::notify_runtime_tile_data_update);
|
||||
|
||||
// --- Shortcuts to methods defined in TileSet ---
|
||||
ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMapLayer::map_pattern);
|
||||
|
|
@ -1824,6 +1871,9 @@ void TileMapLayer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "visibility_mode"), &TileMapLayer::set_collision_visibility_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMapLayer::get_collision_visibility_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_occlusion_enabled", "enabled"), &TileMapLayer::set_occlusion_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_occlusion_enabled"), &TileMapLayer::is_occlusion_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_navigation_enabled", "enabled"), &TileMapLayer::set_navigation_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_navigation_enabled"), &TileMapLayer::is_navigation_enabled);
|
||||
ClassDB::bind_method(D_METHOD("set_navigation_map", "map"), &TileMapLayer::set_navigation_map);
|
||||
|
|
@ -1833,12 +1883,14 @@ void TileMapLayer::_bind_methods() {
|
|||
|
||||
GDVIRTUAL_BIND(_use_tile_data_runtime_update, "coords");
|
||||
GDVIRTUAL_BIND(_tile_data_runtime_update, "coords", "tile_data");
|
||||
GDVIRTUAL_BIND(_update_cells, "coords", "forced_cleanup");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "tile_map_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tile_map_data_from_array", "get_tile_map_data_as_array");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tile_set", "get_tile_set");
|
||||
ADD_GROUP("Rendering", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "occlusion_enabled"), "set_occlusion_enabled", "is_occlusion_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "x_draw_order_reversed"), "set_x_draw_order_reversed", "is_x_draw_order_reversed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "rendering_quadrant_size"), "set_rendering_quadrant_size", "get_rendering_quadrant_size");
|
||||
|
|
@ -2490,6 +2542,18 @@ Rect2i TileMapLayer::get_used_rect() const {
|
|||
return used_rect_cache;
|
||||
}
|
||||
|
||||
bool TileMapLayer::is_cell_flipped_h(const Vector2i &p_coords) const {
|
||||
return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_H;
|
||||
}
|
||||
|
||||
bool TileMapLayer::is_cell_flipped_v(const Vector2i &p_coords) const {
|
||||
return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_V;
|
||||
}
|
||||
|
||||
bool TileMapLayer::is_cell_transposed(const Vector2i &p_coords) const {
|
||||
return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
|
||||
}
|
||||
|
||||
Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), nullptr);
|
||||
|
||||
|
|
@ -2944,6 +3008,20 @@ TileMapLayer::DebugVisibilityMode TileMapLayer::get_collision_visibility_mode()
|
|||
return collision_visibility_mode;
|
||||
}
|
||||
|
||||
void TileMapLayer::set_occlusion_enabled(bool p_enabled) {
|
||||
if (occlusion_enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
occlusion_enabled = p_enabled;
|
||||
dirty.flags[DIRTY_FLAGS_LAYER_OCCLUSION_ENABLED] = true;
|
||||
_queue_internal_update();
|
||||
emit_signal(CoreStringName(changed));
|
||||
}
|
||||
|
||||
bool TileMapLayer::is_occlusion_enabled() const {
|
||||
return occlusion_enabled;
|
||||
}
|
||||
|
||||
void TileMapLayer::set_navigation_enabled(bool p_enabled) {
|
||||
if (navigation_enabled == p_enabled) {
|
||||
return;
|
||||
|
|
@ -2991,6 +3069,114 @@ TileMapLayer::DebugVisibilityMode TileMapLayer::get_navigation_visibility_mode()
|
|||
return navigation_visibility_mode;
|
||||
}
|
||||
|
||||
void TileMapLayer::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&TileMapLayer::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer2D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer2D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void TileMapLayer::navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) {
|
||||
TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node);
|
||||
|
||||
if (tile_map_layer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<TileSet> tile_set = tile_map_layer->get_tile_set();
|
||||
if (tile_set.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int physics_layers_count = tile_set->get_physics_layers_count();
|
||||
int navigation_layers_count = tile_set->get_navigation_layers_count();
|
||||
if (physics_layers_count <= 0 && navigation_layers_count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask();
|
||||
|
||||
const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tile_map_layer->get_global_transform();
|
||||
|
||||
TypedArray<Vector2i> used_cells = tile_map_layer->get_used_cells();
|
||||
for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) {
|
||||
const Vector2i &cell = used_cells[used_cell_index];
|
||||
|
||||
const TileData *tile_data = tile_map_layer->get_cell_tile_data(cell);
|
||||
if (tile_data == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Transform flags.
|
||||
const int alternative_id = tile_map_layer->get_cell_alternative_tile(cell);
|
||||
bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H);
|
||||
bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V);
|
||||
bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE);
|
||||
|
||||
Transform2D tile_transform;
|
||||
tile_transform.set_origin(tile_map_layer->map_to_local(cell));
|
||||
|
||||
const Transform2D tile_transform_offset = tilemap_xform * tile_transform;
|
||||
|
||||
// Parse traversable polygons.
|
||||
for (int navigation_layer = 0; navigation_layer < navigation_layers_count; navigation_layer++) {
|
||||
Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer, flip_h, flip_v, transpose);
|
||||
if (navigation_polygon.is_valid()) {
|
||||
for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) {
|
||||
const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index);
|
||||
if (navigation_polygon_outline.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<Vector2> traversable_outline;
|
||||
traversable_outline.resize(navigation_polygon_outline.size());
|
||||
|
||||
const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr();
|
||||
Vector2 *traversable_outline_ptrw = traversable_outline.ptrw();
|
||||
|
||||
for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) {
|
||||
traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->_add_traversable_outline(traversable_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse obstacles.
|
||||
for (int physics_layer = 0; physics_layer < physics_layers_count; physics_layer++) {
|
||||
if ((parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) &&
|
||||
(tile_set->get_physics_layer_collision_layer(physics_layer) & parsed_collision_mask)) {
|
||||
for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(physics_layer); collision_polygon_index++) {
|
||||
PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(physics_layer, collision_polygon_index);
|
||||
if (collision_polygon_points.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flip_h || flip_v || transpose) {
|
||||
collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose);
|
||||
}
|
||||
|
||||
Vector<Vector2> obstruction_outline;
|
||||
obstruction_outline.resize(collision_polygon_points.size());
|
||||
|
||||
const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr();
|
||||
Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw();
|
||||
|
||||
for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) {
|
||||
obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]);
|
||||
}
|
||||
|
||||
p_source_geometry_data->_add_obstruction_outline(obstruction_outline);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TileMapLayer::TileMapLayer() {
|
||||
set_notify_transform(true);
|
||||
}
|
||||
|
|
@ -3004,7 +3190,7 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
|
|||
HashMap<Vector2i, TileSet::CellNeighbor> output;
|
||||
|
||||
ERR_FAIL_COND_V(is_center_bit(), output);
|
||||
ERR_FAIL_COND_V(!tile_set.is_valid(), output);
|
||||
ERR_FAIL_COND_V(tile_set.is_null(), output);
|
||||
|
||||
TileSet::TileShape shape = tile_set->get_tile_shape();
|
||||
if (shape == TileSet::TILE_SHAPE_SQUARE) {
|
||||
|
|
@ -3108,7 +3294,7 @@ HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coor
|
|||
}
|
||||
|
||||
TerrainConstraint::TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain) {
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
ERR_FAIL_COND(p_tile_set.is_null());
|
||||
tile_set = p_tile_set;
|
||||
bit = 0;
|
||||
base_cell_coords = p_position;
|
||||
|
|
@ -3117,7 +3303,7 @@ TerrainConstraint::TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_
|
|||
|
||||
TerrainConstraint::TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
|
||||
// The way we build the constraint make it easy to detect conflicting constraints.
|
||||
ERR_FAIL_COND(!p_tile_set.is_valid());
|
||||
ERR_FAIL_COND(p_tile_set.is_null());
|
||||
tile_set = p_tile_set;
|
||||
|
||||
TileSet::TileShape shape = tile_set->get_tile_shape();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "scene/resources/2d/tile_set.h"
|
||||
|
||||
class NavigationMeshSourceGeometryData2D;
|
||||
class TileSetAtlasSource;
|
||||
class TileMap;
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ public:
|
|||
|
||||
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, int p_terrain); // For the center terrain bit
|
||||
TerrainConstraint(Ref<TileSet> p_tile_set, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits
|
||||
TerrainConstraint(){};
|
||||
TerrainConstraint() {}
|
||||
};
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
@ -108,7 +109,7 @@ struct CellData {
|
|||
// Rendering.
|
||||
Ref<RenderingQuadrant> rendering_quadrant;
|
||||
SelfList<CellData> rendering_quadrant_list_element;
|
||||
LocalVector<RID> occluders;
|
||||
LocalVector<LocalVector<RID>> occluders;
|
||||
|
||||
// Physics.
|
||||
LocalVector<RID> bodies;
|
||||
|
|
@ -254,6 +255,7 @@ public:
|
|||
DIRTY_FLAGS_LAYER_COLLISION_ENABLED,
|
||||
DIRTY_FLAGS_LAYER_USE_KINEMATIC_BODIES,
|
||||
DIRTY_FLAGS_LAYER_COLLISION_VISIBILITY_MODE,
|
||||
DIRTY_FLAGS_LAYER_OCCLUSION_ENABLED,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_MAP,
|
||||
DIRTY_FLAGS_LAYER_NAVIGATION_VISIBILITY_MODE,
|
||||
|
|
@ -288,6 +290,8 @@ private:
|
|||
bool use_kinematic_bodies = false;
|
||||
DebugVisibilityMode collision_visibility_mode = DEBUG_VISIBILITY_MODE_DEFAULT;
|
||||
|
||||
bool occlusion_enabled = true;
|
||||
|
||||
bool navigation_enabled = true;
|
||||
RID navigation_map_override;
|
||||
DebugVisibilityMode navigation_visibility_mode = DEBUG_VISIBILITY_MODE_DEFAULT;
|
||||
|
|
@ -318,6 +322,7 @@ private:
|
|||
bool _runtime_update_needs_all_cells_cleaned_up = false;
|
||||
void _clear_runtime_update_tile_data();
|
||||
void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data);
|
||||
void _update_cells_callback(bool p_force_cleanup);
|
||||
|
||||
// Per-system methods.
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
|
@ -438,6 +443,10 @@ public:
|
|||
TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
|
||||
Rect2i get_used_rect() const;
|
||||
|
||||
bool is_cell_flipped_h(const Vector2i &p_coords) const;
|
||||
bool is_cell_flipped_v(const Vector2i &p_coords) const;
|
||||
bool is_cell_transposed(const Vector2i &p_coords) const;
|
||||
|
||||
// Patterns.
|
||||
Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
|
||||
void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
|
||||
|
|
@ -455,6 +464,7 @@ public:
|
|||
void notify_runtime_tile_data_update();
|
||||
GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i);
|
||||
GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *);
|
||||
GDVIRTUAL2(_update_cells, TypedArray<Vector2i>, bool);
|
||||
|
||||
// --- Shortcuts to methods defined in TileSet ---
|
||||
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
|
||||
|
|
@ -493,6 +503,9 @@ public:
|
|||
void set_collision_visibility_mode(DebugVisibilityMode p_show_collision);
|
||||
DebugVisibilityMode get_collision_visibility_mode() const;
|
||||
|
||||
void set_occlusion_enabled(bool p_enabled);
|
||||
bool is_occlusion_enabled() const;
|
||||
|
||||
void set_navigation_enabled(bool p_enabled);
|
||||
bool is_navigation_enabled() const;
|
||||
void set_navigation_map(RID p_map);
|
||||
|
|
@ -500,6 +513,14 @@ public:
|
|||
void set_navigation_visibility_mode(DebugVisibilityMode p_show_navigation);
|
||||
DebugVisibilityMode get_navigation_visibility_mode() const;
|
||||
|
||||
private:
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node);
|
||||
|
||||
TileMapLayer();
|
||||
~TileMapLayer();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "touch_screen_button.h"
|
||||
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void TouchScreenButton::set_texture_normal(const Ref<Texture2D> &p_texture) {
|
||||
if (texture_normal == p_texture) {
|
||||
|
|
@ -185,6 +185,7 @@ void TouchScreenButton::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_PAUSED: {
|
||||
if (is_pressed()) {
|
||||
_release();
|
||||
|
|
@ -329,7 +330,7 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 TouchScreenButton::_edit_get_rect() const {
|
||||
if (texture_normal.is_null()) {
|
||||
return CanvasItem::_edit_get_rect();
|
||||
|
|
@ -339,9 +340,9 @@ Rect2 TouchScreenButton::_edit_get_rect() const {
|
|||
}
|
||||
|
||||
bool TouchScreenButton::_edit_use_rect() const {
|
||||
return !texture_normal.is_null();
|
||||
return texture_normal.is_valid();
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
Rect2 TouchScreenButton::get_anchorable_rect() const {
|
||||
if (texture_normal.is_null()) {
|
||||
|
|
@ -429,6 +430,6 @@ void TouchScreenButton::_bind_methods() {
|
|||
}
|
||||
|
||||
TouchScreenButton::TouchScreenButton() {
|
||||
unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D));
|
||||
unit_rect.instantiate();
|
||||
unit_rect->set_size(Vector2(1, 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,10 +76,10 @@ protected:
|
|||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_texture_normal(const Ref<Texture2D> &p_texture);
|
||||
Ref<Texture2D> get_texture_normal() const;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "visible_on_screen_notifier_2d.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const {
|
||||
return rect;
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const {
|
|||
bool VisibleOnScreenNotifier2D::_edit_use_rect() const {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void VisibleOnScreenNotifier2D::_visibility_enter() {
|
||||
if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
|
||||
|
|
|
|||
|
|
@ -54,10 +54,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void set_rect(const Rect2 &p_rect);
|
||||
Rect2 get_rect() const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue