summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/animated_sprite.cpp50
-rw-r--r--scene/2d/animated_sprite.h6
-rw-r--r--scene/2d/area_2d.cpp36
-rw-r--r--scene/2d/audio_stream_player_2d.cpp41
-rw-r--r--scene/2d/audio_stream_player_2d.h9
-rw-r--r--scene/2d/camera_2d.cpp47
-rw-r--r--scene/2d/camera_2d.h3
-rw-r--r--scene/2d/canvas_item.cpp184
-rw-r--r--scene/2d/canvas_item.h11
-rw-r--r--scene/2d/canvas_modulate.cpp2
-rw-r--r--scene/2d/collision_object_2d.cpp30
-rw-r--r--scene/2d/collision_object_2d.h3
-rw-r--r--scene/2d/collision_polygon_2d.cpp6
-rw-r--r--scene/2d/collision_shape_2d.cpp26
-rw-r--r--scene/2d/collision_shape_2d.h2
-rw-r--r--scene/2d/cpu_particles_2d.cpp322
-rw-r--r--scene/2d/cpu_particles_2d.h23
-rw-r--r--scene/2d/joints_2d.cpp10
-rw-r--r--scene/2d/light_2d.cpp6
-rw-r--r--scene/2d/light_occluder_2d.cpp63
-rw-r--r--scene/2d/light_occluder_2d.h9
-rw-r--r--scene/2d/line_2d.cpp125
-rw-r--r--scene/2d/line_2d.h13
-rw-r--r--scene/2d/line_builder.cpp120
-rw-r--r--scene/2d/line_builder.h1
-rw-r--r--scene/2d/mesh_instance_2d.cpp2
-rw-r--r--scene/2d/mesh_instance_2d.h2
-rw-r--r--scene/2d/multimesh_instance_2d.cpp111
-rw-r--r--scene/2d/multimesh_instance_2d.h65
-rw-r--r--scene/2d/navigation_2d.cpp13
-rw-r--r--scene/2d/navigation_polygon.cpp4
-rw-r--r--scene/2d/node_2d.cpp2
-rw-r--r--scene/2d/parallax_layer.cpp8
-rw-r--r--scene/2d/particles_2d.cpp32
-rw-r--r--scene/2d/particles_2d.h2
-rw-r--r--scene/2d/path_2d.cpp42
-rw-r--r--scene/2d/physics_body_2d.cpp106
-rw-r--r--scene/2d/physics_body_2d.h4
-rw-r--r--scene/2d/polygon_2d.cpp8
-rw-r--r--scene/2d/position_2d.cpp37
-rw-r--r--scene/2d/position_2d.h7
-rw-r--r--scene/2d/ray_cast_2d.cpp23
-rw-r--r--scene/2d/remote_transform_2d.cpp7
-rw-r--r--scene/2d/remote_transform_2d.h2
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/skeleton_2d.h8
-rw-r--r--scene/2d/sprite.cpp48
-rw-r--r--scene/2d/sprite.h7
-rw-r--r--scene/2d/tile_map.cpp472
-rw-r--r--scene/2d/tile_map.h30
-rw-r--r--scene/2d/visibility_notifier_2d.cpp2
51 files changed, 1494 insertions, 702 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 932db8f001..9d02408641 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "animated_sprite.h"
+
#include "core/os/os.h"
#include "scene/scene_string_names.h"
@@ -69,10 +70,7 @@ bool AnimatedSprite::_edit_use_rect() const {
Ref<Texture> t;
if (animation)
t = frames->get_frame(animation, frame);
- if (t.is_null())
- return false;
-
- return true;
+ return t.is_valid();
}
Rect2 AnimatedSprite::get_anchorable_rect() const {
@@ -104,7 +102,7 @@ Rect2 AnimatedSprite::_get_rect() const {
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
if (p_at_pos >= 0 && p_at_pos < E->get().frames.size())
E->get().frames.insert(p_at_pos, p_frame);
@@ -116,7 +114,7 @@ void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_fra
int SpriteFrames::get_frame_count(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, 0);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().frames.size();
}
@@ -124,7 +122,7 @@ int SpriteFrames::get_frame_count(const StringName &p_anim) const {
void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().frames.remove(p_idx);
emit_changed();
@@ -132,7 +130,7 @@ void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
void SpriteFrames::clear(const StringName &p_anim) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().frames.clear();
emit_changed();
@@ -146,7 +144,7 @@ void SpriteFrames::clear_all() {
void SpriteFrames::add_animation(const StringName &p_anim) {
- ERR_FAIL_COND(animations.has(p_anim));
+ ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'.");
animations[p_anim] = Anim();
animations[p_anim].normal_name = String(p_anim) + NORMAL_SUFFIX;
@@ -163,8 +161,8 @@ void SpriteFrames::remove_animation(const StringName &p_anim) {
void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) {
- ERR_FAIL_COND(!animations.has(p_prev));
- ERR_FAIL_COND(animations.has(p_next));
+ ERR_FAIL_COND_MSG(!animations.has(p_prev), "SpriteFrames doesn't have animation '" + String(p_prev) + "'.");
+ ERR_FAIL_COND_MSG(animations.has(p_next), "Animation '" + String(p_next) + "' already exists.");
Anim anim = animations[p_prev];
animations.erase(p_prev);
@@ -204,26 +202,26 @@ Vector<String> SpriteFrames::get_animation_names() const {
void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) {
- ERR_FAIL_COND(p_fps < 0);
+ ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ").");
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().speed = p_fps;
}
float SpriteFrames::get_animation_speed(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, 0);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().speed;
}
void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().loop = p_loop;
}
bool SpriteFrames::get_animation_loop(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, false);
+ ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().loop;
}
@@ -359,12 +357,11 @@ void AnimatedSprite::_validate_property(PropertyInfo &property) const {
}
if (property.name == "frame") {
-
- property.hint = PROPERTY_HINT_SPRITE_FRAME;
-
+ property.hint = PROPERTY_HINT_RANGE;
if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) {
property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1";
}
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
@@ -607,10 +604,14 @@ bool AnimatedSprite::_is_playing() const {
void AnimatedSprite::play(const StringName &p_animation, const bool p_backwards) {
- if (p_animation)
+ backwards = p_backwards;
+
+ if (p_animation) {
set_animation(p_animation);
+ if (backwards && get_frame() == 0)
+ set_frame(frames->get_frame_count(p_animation) - 1);
+ }
- backwards = p_backwards;
_set_playing(true);
}
@@ -645,6 +646,9 @@ void AnimatedSprite::_reset_timeout() {
void AnimatedSprite::set_animation(const StringName &p_animation) {
+ ERR_FAIL_COND_MSG(frames == NULL, vformat("There is no animation with name '%s'.", p_animation));
+ ERR_FAIL_COND_MSG(frames->get_animation_names().find(p_animation) == -1, vformat("There is no animation with name '%s'.", p_animation));
+
if (animation == p_animation)
return;
@@ -662,7 +666,7 @@ StringName AnimatedSprite::get_animation() const {
String AnimatedSprite::get_configuration_warning() const {
if (frames.is_null()) {
- return TTR("A SpriteFrames resource must be created or set in the 'Frames' property in order for AnimatedSprite to display frames.");
+ return TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite to display frames.");
}
return String();
@@ -708,7 +712,7 @@ void AnimatedSprite::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 2cc372bd93..3192d44678 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -85,7 +85,7 @@ public:
_FORCE_INLINE_ Ref<Texture> get_frame(const StringName &p_anim, int p_idx) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, Ref<Texture>());
+ ERR_FAIL_COND_V_MSG(!E, Ref<Texture>(), "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND_V(p_idx < 0, Ref<Texture>());
if (p_idx >= E->get().frames.size())
return Ref<Texture>();
@@ -96,7 +96,7 @@ public:
_FORCE_INLINE_ Ref<Texture> get_normal_frame(const StringName &p_anim, int p_idx) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, Ref<Texture>());
+ ERR_FAIL_COND_V_MSG(!E, Ref<Texture>(), "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND_V(p_idx < 0, Ref<Texture>());
const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name);
@@ -109,7 +109,7 @@ public:
void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture> &p_frame) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND(p_idx < 0);
if (p_idx >= E->get().frames.size())
return;
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 2a225e5797..66a1318cb7 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -261,8 +261,9 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_
Map<ObjectID, AreaState>::Element *E = area_map.find(objid);
- ERR_FAIL_COND(!area_in && !E);
-
+ if (!area_in && !E) {
+ return; //likely removed from the tree
+ }
locked = true;
if (area_in) {
@@ -320,10 +321,7 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_
void Area2D::_clear_monitoring() {
- if (locked) {
- ERR_EXPLAIN("This function can't be used during the in/out signal.");
- }
- ERR_FAIL_COND(locked);
+ ERR_FAIL_COND_MSG(locked, "This function can't be used during the in/out signal.");
{
Map<ObjectID, BodyState> bmcopy = body_map;
@@ -400,10 +398,7 @@ void Area2D::set_monitoring(bool p_enable) {
if (p_enable == monitoring)
return;
- if (locked) {
- ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitoring\",true/false)");
- }
- ERR_FAIL_COND(locked);
+ ERR_FAIL_COND_MSG(locked, "Function blocked during in/out signal. Use set_deferred(\"monitoring\", true/false).");
monitoring = p_enable;
@@ -426,10 +421,7 @@ bool Area2D::is_monitoring() const {
void Area2D::set_monitorable(bool p_enable) {
- if (locked || (is_inside_tree() && Physics2DServer::get_singleton()->is_flushing_queries())) {
- ERR_EXPLAIN("Function blocked during in/out signal. Use set_deferred(\"monitorable\",true/false)");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(locked || (is_inside_tree() && Physics2DServer::get_singleton()->is_flushing_queries()), "Function blocked during in/out signal. Use set_deferred(\"monitorable\", true/false).");
if (p_enable == monitorable)
return;
@@ -446,7 +438,7 @@ bool Area2D::is_monitorable() const {
Array Area2D::get_overlapping_bodies() const {
- ERR_FAIL_COND_V(!monitoring, Array());
+ ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off.");
Array ret;
ret.resize(body_map.size());
int idx = 0;
@@ -464,7 +456,7 @@ Array Area2D::get_overlapping_bodies() const {
Array Area2D::get_overlapping_areas() const {
- ERR_FAIL_COND_V(!monitoring, Array());
+ ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off.");
Array ret;
ret.resize(area_map.size());
int idx = 0;
@@ -656,10 +648,10 @@ void Area2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout);
ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout);
- ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsBody2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape")));
- ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsBody2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape")));
- ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsBody2D")));
- ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsBody2D")));
+ ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape")));
+ ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape")));
+ ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape")));
ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape")));
@@ -671,8 +663,8 @@ void Area2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_linear_damp", "get_linear_damp");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 7b28ad2c12..932af469d0 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -37,7 +37,7 @@
void AudioStreamPlayer2D::_mix_audio() {
if (!stream_playback.is_valid() || !active ||
- (stream_paused && !stream_fade_out)) {
+ (stream_paused && !stream_paused_fade_out)) {
return;
}
@@ -50,7 +50,7 @@ void AudioStreamPlayer2D::_mix_audio() {
AudioFrame *buffer = mix_buffer.ptrw();
int buffer_size = mix_buffer.size();
- if (stream_fade_out) {
+ if (stream_paused_fade_out) {
// Short fadeout ramp
buffer_size = MIN(buffer_size, 128);
}
@@ -84,10 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() {
}
//mix!
- AudioFrame target_volume = stream_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
- AudioFrame vol_prev = stream_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
+ AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol;
AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size);
- AudioFrame vol = stream_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
+ AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol;
int cc = AudioServer::get_singleton()->get_channel_count();
@@ -139,15 +139,9 @@ void AudioStreamPlayer2D::_mix_audio() {
active = false;
}
- if (stream_stop) {
- active = false;
- set_physics_process_internal(false);
- setplay = -1;
- }
-
output_ready = false;
- stream_fade_in = false;
- stream_fade_out = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
}
void AudioStreamPlayer2D::_notification(int p_what) {
@@ -329,7 +323,6 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
}
if (stream_playback.is_valid()) {
- stream_stop = false;
active = true;
setplay = p_from_pos;
output_ready = false;
@@ -347,8 +340,9 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
void AudioStreamPlayer2D::stop() {
if (stream_playback.is_valid()) {
- stream_stop = true;
- stream_fade_out = true;
+ active = false;
+ set_physics_process_internal(false);
+ setplay = -1;
}
}
@@ -463,8 +457,8 @@ void AudioStreamPlayer2D::set_stream_paused(bool p_pause) {
if (p_pause != stream_paused) {
stream_paused = p_pause;
- stream_fade_in = p_pause ? false : true;
- stream_fade_out = p_pause ? true : false;
+ stream_paused_fade_in = !p_pause;
+ stream_paused_fade_out = p_pause;
}
}
@@ -473,6 +467,10 @@ bool AudioStreamPlayer2D::get_stream_paused() const {
return stream_paused;
}
+Ref<AudioStreamPlayback> AudioStreamPlayer2D::get_stream_playback() {
+ return stream_playback;
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
@@ -512,6 +510,8 @@ void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream_paused", "pause"), &AudioStreamPlayer2D::set_stream_paused);
ClassDB::bind_method(D_METHOD("get_stream_paused"), &AudioStreamPlayer2D::get_stream_paused);
+ ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer2D::get_stream_playback);
+
ClassDB::bind_method(D_METHOD("_bus_layout_changed"), &AudioStreamPlayer2D::_bus_layout_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
@@ -543,9 +543,8 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() {
output_ready = false;
area_mask = 1;
stream_paused = false;
- stream_fade_in = false;
- stream_fade_out = false;
- stream_stop = false;
+ stream_paused_fade_in = false;
+ stream_paused_fade_out = false;
AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed");
}
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index caf5c6ee49..ffa3d4edb4 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -37,7 +37,7 @@
class AudioStreamPlayer2D : public Node2D {
- GDCLASS(AudioStreamPlayer2D, Node2D)
+ GDCLASS(AudioStreamPlayer2D, Node2D);
private:
enum {
@@ -73,9 +73,8 @@ private:
float pitch_scale;
bool autoplay;
bool stream_paused;
- bool stream_fade_in;
- bool stream_fade_out;
- bool stream_stop;
+ bool stream_paused_fade_in;
+ bool stream_paused_fade_out;
StringName bus;
void _mix_audio();
@@ -130,6 +129,8 @@ public:
void set_stream_paused(bool p_pause);
bool get_stream_paused() const;
+ Ref<AudioStreamPlayback> get_stream_playback();
+
AudioStreamPlayer2D();
~AudioStreamPlayer2D();
};
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 11846654c5..3e8902314c 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -29,10 +29,11 @@
/*************************************************************************/
#include "camera_2d.h"
+
+#include "core/engine.h"
#include "core/math/math_funcs.h"
#include "scene/scene_string_names.h"
#include "servers/visual_server.h"
-#include <editor/editor_node.h>
void Camera2D::_update_scroll() {
@@ -44,15 +45,16 @@ void Camera2D::_update_scroll() {
return;
}
+ if (!viewport)
+ return;
+
if (current) {
ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id));
Transform2D xform = get_camera_transform();
- if (viewport) {
- viewport->set_canvas_transform(xform);
- }
+ viewport->set_canvas_transform(xform);
Size2 screen_size = viewport->get_visible_rect().size;
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2());
@@ -104,7 +106,7 @@ Transform2D Camera2D::get_camera_transform() {
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
- if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
+ if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !h_offset_changed) {
camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_LEFT]));
camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_RIGHT]));
} else {
@@ -114,9 +116,11 @@ Transform2D Camera2D::get_camera_transform() {
} else {
camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs;
}
+
+ h_offset_changed = false;
}
- if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
+ if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint() && !v_offset_changed) {
camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_TOP]));
camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_BOTTOM]));
@@ -128,6 +132,8 @@ Transform2D Camera2D::get_camera_transform() {
} else {
camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs;
}
+
+ v_offset_changed = false;
}
} else if (anchor_mode == ANCHOR_MODE_FIXED_TOP_LEFT) {
@@ -138,9 +144,6 @@ Transform2D Camera2D::get_camera_transform() {
Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5 * zoom) : Point2());
Rect2 screen_rect(-screen_offset + camera_pos, screen_size * zoom);
- if (offset != Vector2())
- screen_rect.position += offset;
-
if (limit_smoothing_enabled) {
if (screen_rect.position.x < limit[MARGIN_LEFT])
camera_pos.x -= screen_rect.position.x - limit[MARGIN_LEFT];
@@ -191,21 +194,8 @@ Transform2D Camera2D::get_camera_transform() {
if (screen_rect.position.y < limit[MARGIN_TOP])
screen_rect.position.y = limit[MARGIN_TOP];
- if (offset != Vector2()) {
-
+ if (offset != Vector2())
screen_rect.position += offset;
- if (screen_rect.position.x + screen_rect.size.x > limit[MARGIN_RIGHT])
- screen_rect.position.x = limit[MARGIN_RIGHT] - screen_rect.size.x;
-
- if (screen_rect.position.y + screen_rect.size.y > limit[MARGIN_BOTTOM])
- screen_rect.position.y = limit[MARGIN_BOTTOM] - screen_rect.size.y;
-
- if (screen_rect.position.x < limit[MARGIN_LEFT])
- screen_rect.position.x = limit[MARGIN_LEFT];
-
- if (screen_rect.position.y < limit[MARGIN_TOP])
- screen_rect.position.y = limit[MARGIN_TOP];
- }
camera_screen_center = screen_rect.position + screen_rect.size * 0.5;
@@ -568,6 +558,7 @@ bool Camera2D::is_v_drag_enabled() const {
void Camera2D::set_v_offset(float p_offset) {
v_ofs = p_offset;
+ v_offset_changed = true;
_update_scroll();
}
@@ -579,6 +570,7 @@ float Camera2D::get_v_offset() const {
void Camera2D::set_h_offset(float p_offset) {
h_ofs = p_offset;
+ h_offset_changed = true;
_update_scroll();
}
float Camera2D::get_h_offset() const {
@@ -764,8 +756,8 @@ void Camera2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "smoothing_speed"), "set_follow_smoothing", "get_follow_smoothing");
ADD_GROUP("Offset", "offset_");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_v", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_h", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_h_offset", "get_h_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset_v", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_v_offset", "get_v_offset");
ADD_GROUP("Drag Margin", "drag_margin_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_LEFT);
@@ -813,9 +805,12 @@ Camera2D::Camera2D() {
limit_drawing_enabled = false;
margin_drawing_enabled = false;
- h_drag_enabled = true;
- v_drag_enabled = true;
+ h_drag_enabled = false;
+ v_drag_enabled = false;
h_ofs = 0;
v_ofs = 0;
+ h_offset_changed = false;
+ v_offset_changed = false;
+
set_notify_transform(true);
}
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
index 7f16ecff41..bb3c76b30c 100644
--- a/scene/2d/camera_2d.h
+++ b/scene/2d/camera_2d.h
@@ -77,6 +77,9 @@ protected:
float h_ofs;
float v_ofs;
+ bool h_offset_changed;
+ bool v_offset_changed;
+
Point2 camera_screen_center;
void _update_process_mode();
void _update_scroll();
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 2739f71543..fa23e3fd39 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -360,6 +360,10 @@ bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tole
}
}
+Transform2D CanvasItem::_edit_get_transform() const {
+ return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot());
+}
+
bool CanvasItem::is_visible_in_tree() const {
if (!is_inside_tree())
@@ -602,9 +606,7 @@ void CanvasItem::_notification(int p_what) {
}
global_invalid = true;
} break;
- case NOTIFICATION_DRAW: {
-
- } break;
+ case NOTIFICATION_DRAW:
case NOTIFICATION_TRANSFORM_CHANGED: {
} break;
@@ -641,6 +643,9 @@ void CanvasItem::update() {
void CanvasItem::set_modulate(const Color &p_modulate) {
+ if (modulate == p_modulate)
+ return;
+
modulate = p_modulate;
VisualServer::get_singleton()->canvas_item_set_modulate(canvas_item, modulate);
}
@@ -679,6 +684,9 @@ CanvasItem *CanvasItem::get_parent_item() const {
void CanvasItem::set_self_modulate(const Color &p_self_modulate) {
+ if (self_modulate == p_self_modulate)
+ return;
+
self_modulate = p_self_modulate;
VisualServer::get_singleton()->canvas_item_set_self_modulate(canvas_item, self_modulate);
}
@@ -689,6 +697,9 @@ Color CanvasItem::get_self_modulate() const {
void CanvasItem::set_light_mask(int p_light_mask) {
+ if (light_mask == p_light_mask)
+ return;
+
light_mask = p_light_mask;
VS::get_singleton()->canvas_item_set_light_mask(canvas_item, p_light_mask);
}
@@ -707,20 +718,14 @@ void CanvasItem::item_rect_changed(bool p_size_changed) {
void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
}
void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
Vector<Color> colors;
colors.push_back(p_color);
@@ -729,21 +734,28 @@ void CanvasItem::draw_polyline(const Vector<Point2> &p_points, const Color &p_co
void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased);
}
-void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
+void CanvasItem::draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
+ Vector<Point2> points;
+ points.resize(p_point_count);
+ const float delta_angle = p_end_angle - p_start_angle;
+ for (int i = 0; i < p_point_count; i++) {
+ float theta = (i / (p_point_count - 1.0f)) * delta_angle + p_start_angle;
+ points.set(i, p_center + Vector2(Math::cos(theta), Math::sin(theta)) * p_radius);
}
+ draw_polyline(points, p_color, p_width, p_antialiased);
+}
+
+void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
+
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+
Vector<Color> colors;
colors.push_back(p_color);
VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width, p_antialiased);
@@ -751,48 +763,76 @@ void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_c
void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width, p_antialiased);
}
-void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
+void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled, float p_width, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
if (p_filled) {
+ if (p_width != 1.0) {
+ WARN_PRINT("The draw_rect() \"width\" argument has no effect when \"filled\" is \"true\".");
+ }
+
+ if (p_antialiased) {
+ WARN_PRINT("The draw_rect() \"antialiased\" argument has no effect when \"filled\" is \"true\".");
+ }
VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
} else {
- VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(p_rect.size.width, 0), p_color);
- VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(0, p_rect.size.height), p_color);
- VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(0, p_rect.size.height), p_rect.position + p_rect.size, p_color);
- VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(p_rect.size.width, 0), p_rect.position + p_rect.size, p_color);
+ // Thick lines are offset depending on their width to avoid partial overlapping.
+ // Thin lines don't require an offset, so don't apply one in this case
+ float offset;
+ if (p_width >= 2) {
+ offset = p_width / 2.0;
+ } else {
+ offset = 0.0;
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_line(
+ canvas_item,
+ p_rect.position + Size2(-offset, 0),
+ p_rect.position + Size2(p_rect.size.width + offset, 0),
+ p_color,
+ p_width,
+ p_antialiased);
+ VisualServer::get_singleton()->canvas_item_add_line(
+ canvas_item,
+ p_rect.position + Size2(p_rect.size.width, offset),
+ p_rect.position + Size2(p_rect.size.width, p_rect.size.height - offset),
+ p_color,
+ p_width,
+ p_antialiased);
+ VisualServer::get_singleton()->canvas_item_add_line(
+ canvas_item,
+ p_rect.position + Size2(p_rect.size.width + offset, p_rect.size.height),
+ p_rect.position + Size2(-offset, p_rect.size.height),
+ p_color,
+ p_width,
+ p_antialiased);
+ VisualServer::get_singleton()->canvas_item_add_line(
+ canvas_item,
+ p_rect.position + Size2(0, p_rect.size.height - offset),
+ p_rect.position + Size2(0, offset),
+ p_color,
+ p_width,
+ p_antialiased);
}
}
void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
VisualServer::get_singleton()->canvas_item_add_circle(canvas_item, p_pos, p_radius, p_color);
}
void CanvasItem::draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate, const Ref<Texture> &p_normal_map) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
@@ -801,29 +841,20 @@ void CanvasItem::draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos
void CanvasItem::draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose, p_normal_map);
}
void CanvasItem::draw_texture_rect_region(const Ref<Texture> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map, bool p_clip_uv) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_texture.is_null());
p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose, p_normal_map, p_clip_uv);
}
void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_style_box.is_null());
@@ -831,10 +862,7 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p
}
void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture> p_texture, float p_width, const Ref<Texture> &p_normal_map) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
@@ -843,10 +871,7 @@ void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Col
}
void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
Transform2D xform(p_rot, p_offset);
xform.scale_basis(p_scale);
@@ -855,20 +880,14 @@ void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const S
void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
VisualServer::get_singleton()->canvas_item_add_set_transform(canvas_item, p_matrix);
}
void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture> p_texture, const Ref<Texture> &p_normal_map, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RID rid_normal = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
@@ -878,10 +897,7 @@ void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color
void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture> p_texture, const Ref<Texture> &p_normal_map, bool p_antialiased) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
Vector<Color> colors;
colors.push_back(p_color);
@@ -891,13 +907,13 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo
VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased);
}
-void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
+void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform, const Color &p_modulate) {
ERR_FAIL_COND(p_mesh.is_null());
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
- VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid);
+ VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), p_transform, p_modulate, texture_rid, normal_map_rid);
}
void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
@@ -909,10 +925,7 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL();
- }
+ ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_font.is_null());
p_font->draw(canvas_item, p_pos, p_text, p_modulate, p_clip_w);
@@ -920,14 +933,14 @@ void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const
float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, const Color &p_modulate) {
- if (!drawing) {
- ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
- ERR_FAIL_V(0);
- }
+ ERR_FAIL_COND_V_MSG(!drawing, 0, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND_V(p_char.length() != 1, 0);
ERR_FAIL_COND_V(p_font.is_null(), 0);
+ if (p_font->has_outline()) {
+ p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], Color(1, 1, 1), true);
+ }
return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.c_str()[0], p_modulate);
}
@@ -1074,6 +1087,7 @@ Vector2 CanvasItem::make_canvas_position_local(const Vector2 &screen_point) cons
Ref<InputEvent> CanvasItem::make_input_local(const Ref<InputEvent> &p_event) const {
+ ERR_FAIL_COND_V(p_event.is_null(), p_event);
ERR_FAIL_COND_V(!is_inside_tree(), p_event);
return p_event->xformed_by((get_canvas_transform() * get_global_transform()).affine_inverse());
@@ -1123,6 +1137,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_pivot", "pivot"), &CanvasItem::_edit_set_pivot);
ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot);
ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot);
+ ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform);
ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item);
@@ -1155,9 +1170,10 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_arc", "center", "radius", "start_angle", "end_angle", "point_count", "color", "width", "antialiased"), &CanvasItem::draw_arc, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width", "antialiased"), &CanvasItem::draw_multiline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled", "width", "antialiased"), &CanvasItem::draw_rect, DEFVAL(true), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture", "rect", "tile", "modulate", "transpose", "normal_map"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()));
@@ -1168,7 +1184,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1)));
- ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()));
+ ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture", "normal_map"), &CanvasItem::draw_multimesh, DEFVAL(Ref<Texture>()));
ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform);
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index bf7cfa8e75..e51ee601e2 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -46,7 +46,7 @@ class StyleBox;
class CanvasItemMaterial : public Material {
- GDCLASS(CanvasItemMaterial, Material)
+ GDCLASS(CanvasItemMaterial, Material);
public:
enum BlendMode {
@@ -143,7 +143,7 @@ public:
void set_particles_anim_v_frames(int p_frames);
int get_particles_anim_v_frames() const;
- void set_particles_anim_loop(bool p_frames);
+ void set_particles_anim_loop(bool p_loop);
bool get_particles_anim_loop() const;
static void init_shaders();
@@ -281,6 +281,8 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot){};
virtual Point2 _edit_get_pivot() const { return Point2(); };
+ virtual Transform2D _edit_get_transform() const;
+
/* VISIBILITY */
void set_visible(bool p_visible);
@@ -305,9 +307,10 @@ public:
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
+ void draw_arc(const Vector2 &p_center, float p_radius, float p_start_angle, float p_end_angle, int p_point_count, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
- void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
+ void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, float p_width = 1.0, bool p_antialiased = false);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());
void draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>());
@@ -317,7 +320,7 @@ public:
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
- void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
+ void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index bd7bb97b03..009d664462 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -70,7 +70,7 @@ void CanvasModulate::_bind_methods() {
void CanvasModulate::set_color(const Color &p_color) {
color = p_color;
- if (is_inside_tree()) {
+ if (is_visible_in_tree()) {
VS::get_singleton()->canvas_set_modulate(get_canvas(), color);
}
}
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index f43d97eb2a..9f79691405 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -46,8 +46,6 @@ void CollisionObject2D::_notification(int p_what) {
else
Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
- last_transform = global_transform;
-
RID space = get_world_2d()->get_space();
if (area) {
Physics2DServer::get_singleton()->area_set_space(rid, space);
@@ -73,19 +71,17 @@ void CollisionObject2D::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- Transform2D global_transform = get_global_transform();
-
- if (only_update_transform_changes && global_transform == last_transform) {
+ if (only_update_transform_changes) {
return;
}
+ Transform2D global_transform = get_global_transform();
+
if (area)
Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
else
Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform);
- last_transform = global_transform;
-
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -218,12 +214,13 @@ void CollisionObject2D::shape_owner_set_transform(uint32_t p_owner, const Transf
ERR_FAIL_COND(!shapes.has(p_owner));
ShapeData &sd = shapes[p_owner];
+
sd.xform = p_transform;
for (int i = 0; i < sd.shapes.size(); i++) {
if (area) {
- Physics2DServer::get_singleton()->area_set_shape_transform(rid, sd.shapes[i].index, p_transform);
+ Physics2DServer::get_singleton()->area_set_shape_transform(rid, sd.shapes[i].index, sd.xform);
} else {
- Physics2DServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
+ Physics2DServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, sd.xform);
}
}
}
@@ -251,9 +248,9 @@ void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2
s.index = total_subshapes;
s.shape = p_shape;
if (area) {
- Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform);
+ Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
} else {
- Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform);
+ Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
}
sd.shapes.push_back(s);
@@ -375,11 +372,12 @@ void CollisionObject2D::set_only_update_transform_changes(bool p_enable) {
void CollisionObject2D::_update_pickable() {
if (!is_inside_tree())
return;
- bool pickable = this->pickable && is_inside_tree() && is_visible_in_tree();
+
+ bool is_pickable = pickable && is_visible_in_tree();
if (area)
- Physics2DServer::get_singleton()->area_set_pickable(rid, pickable);
+ Physics2DServer::get_singleton()->area_set_pickable(rid, is_pickable);
else
- Physics2DServer::get_singleton()->body_set_pickable(rid, pickable);
+ Physics2DServer::get_singleton()->body_set_pickable(rid, is_pickable);
}
String CollisionObject2D::get_configuration_warning() const {
@@ -387,8 +385,8 @@ String CollisionObject2D::get_configuration_warning() const {
String warning = Node2D::get_configuration_warning();
if (shapes.empty()) {
- if (warning == String()) {
- warning += "\n";
+ if (!warning.empty()) {
+ warning += "\n\n";
}
warning += TTR("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.");
}
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 8aa3330f37..0a9d33b8a6 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -36,7 +36,7 @@
class CollisionObject2D : public Node2D {
- GDCLASS(CollisionObject2D, Node2D)
+ GDCLASS(CollisionObject2D, Node2D);
bool area;
RID rid;
@@ -67,7 +67,6 @@ class CollisionObject2D : public Node2D {
int total_subshapes;
Map<uint32_t, ShapeData> shapes;
- Transform2D last_transform;
bool only_update_transform_changes; //this is used for sync physics in KinematicBody
protected:
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index ef7644fcab..766a8a6de4 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -70,7 +70,7 @@ void CollisionPolygon2D::_build_polygon() {
w[(i << 1) + 1] = polygon[(i + 1) % polygon.size()];
}
- w = PoolVector<Vector2>::Write();
+ w.release();
concave->set_segments(segments);
parent->shape_owner_add_shape(owner_id, concave);
@@ -169,8 +169,8 @@ void CollisionPolygon2D::_notification(int p_what) {
Vector<Vector2> pts;
float tsize = 8;
pts.push_back(line_to + (Vector2(0, tsize)));
- pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
- pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(Math_SQRT12 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-Math_SQRT12 * tsize, 0)));
Vector<Color> cols;
for (int i = 0; i < 3; i++)
cols.push_back(dcol);
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 5440a1d8c3..d9c1d69b53 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -97,15 +97,8 @@ void CollisionShape2D::_notification(int p_what) {
}
owner_id = 0;
parent = NULL;
- } break;
- /*
- case NOTIFICATION_TRANSFORM_CHANGED: {
-
- if (!is_inside_scene())
- break;
- _update_parent();
- } break;*/
+ } break;
case NOTIFICATION_DRAW: {
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
@@ -131,20 +124,23 @@ void CollisionShape2D::_notification(int p_what) {
rect = rect.grow(3);
if (one_way_collision) {
- Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
- dcol.a = 1.0;
+ // Draw an arrow indicating the one-way collision direction
+ draw_col = get_tree()->get_debug_collisions_color().inverted();
+ if (disabled) {
+ draw_col = draw_col.darkened(0.25);
+ }
Vector2 line_to(0, 20);
- draw_line(Vector2(), line_to, dcol, 3);
+ draw_line(Vector2(), line_to, draw_col, 2, true);
Vector<Vector2> pts;
float tsize = 8;
pts.push_back(line_to + (Vector2(0, tsize)));
- pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
- pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(Math_SQRT12 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-Math_SQRT12 * tsize, 0)));
Vector<Color> cols;
for (int i = 0; i < 3; i++)
- cols.push_back(dcol);
+ cols.push_back(draw_col);
- draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ draw_primitive(pts, cols, Vector<Vector2>());
}
} break;
}
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index e913b4a866..26c61f47bd 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -38,7 +38,7 @@ class CollisionObject2D;
class CollisionShape2D : public Node2D {
- GDCLASS(CollisionShape2D, Node2D)
+ GDCLASS(CollisionShape2D, Node2D);
Ref<Shape2D> shape;
Rect2 rect;
uint32_t owner_id;
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 721b52edaa..372d8f614b 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -29,13 +29,17 @@
/*************************************************************************/
#include "cpu_particles_2d.h"
-#include "particles_2d.h"
+
#include "scene/2d/canvas_item.h"
+#include "scene/2d/particles_2d.h"
#include "scene/resources/particles_material.h"
#include "servers/visual_server.h"
void CPUParticles2D::set_emitting(bool p_emitting) {
+ if (emitting == p_emitting)
+ return;
+
emitting = p_emitting;
if (emitting)
set_process_internal(true);
@@ -43,7 +47,7 @@ void CPUParticles2D::set_emitting(bool p_emitting) {
void CPUParticles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles must be greater than 0.");
particles.resize(p_amount);
{
@@ -61,7 +65,7 @@ void CPUParticles2D::set_amount(int p_amount) {
}
void CPUParticles2D::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
}
@@ -82,10 +86,16 @@ void CPUParticles2D::set_randomness_ratio(float p_ratio) {
randomness_ratio = p_ratio;
}
+void CPUParticles2D::set_lifetime_randomness(float p_random) {
+
+ lifetime_randomness = p_random;
+}
void CPUParticles2D::set_use_local_coordinates(bool p_enable) {
local_coords = p_enable;
+ set_notify_transform(!p_enable);
}
+
void CPUParticles2D::set_speed_scale(float p_scale) {
speed_scale = p_scale;
@@ -120,6 +130,10 @@ float CPUParticles2D::get_randomness_ratio() const {
return randomness_ratio;
}
+float CPUParticles2D::get_lifetime_randomness() const {
+
+ return lifetime_randomness;
+}
bool CPUParticles2D::get_use_local_coordinates() const {
@@ -246,6 +260,7 @@ void CPUParticles2D::restart() {
inactive_time = 0;
frame_remainder = 0;
cycle = 0;
+ emitting = false;
{
int pc = particles.size();
@@ -255,6 +270,18 @@ void CPUParticles2D::restart() {
w[i].active = false;
}
}
+
+ set_emitting(true);
+}
+
+void CPUParticles2D::set_direction(Vector2 p_direction) {
+
+ direction = p_direction;
+}
+
+Vector2 CPUParticles2D::get_direction() const {
+
+ return direction;
}
void CPUParticles2D::set_spread(float p_spread) {
@@ -324,9 +351,9 @@ void CPUParticles2D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv
case PARAM_ANGULAR_VELOCITY: {
_adjust_curve_range(p_curve, -360, 360);
} break;
- /*case PARAM_ORBIT_VELOCITY: {
+ case PARAM_ORBIT_VELOCITY: {
_adjust_curve_range(p_curve, -500, 500);
- } break;*/
+ } break;
case PARAM_LINEAR_ACCEL: {
_adjust_curve_range(p_curve, -200, 200);
} break;
@@ -466,7 +493,7 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_CIRCLE) {
+ if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) {
property.usage = 0;
}
@@ -489,12 +516,6 @@ void CPUParticles2D::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_colors" && emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) {
property.usage = 0;
}
-
- /*
- if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
- property.usage = 0;
- }
- */
}
static uint32_t idhash(uint32_t x) {
@@ -518,6 +539,74 @@ static float rand_from_seed(uint32_t &seed) {
return float(seed % uint32_t(65536)) / 65535.0;
}
+void CPUParticles2D::_update_internal() {
+
+ if (particles.size() == 0 || !is_visible_in_tree()) {
+ _set_redraw(false);
+ return;
+ }
+
+ float delta = get_process_delta_time();
+ if (emitting) {
+ inactive_time = 0;
+ } else {
+ inactive_time += delta;
+ if (inactive_time > lifetime * 1.2) {
+ set_process_internal(false);
+ _set_redraw(false);
+
+ //reset variables
+ time = 0;
+ inactive_time = 0;
+ frame_remainder = 0;
+ cycle = 0;
+ return;
+ }
+ }
+ _set_redraw(true);
+
+ if (time == 0 && pre_process_time > 0.0) {
+
+ float frame_time;
+ if (fixed_fps > 0)
+ frame_time = 1.0 / fixed_fps;
+ else
+ frame_time = 1.0 / 30.0;
+
+ float todo = pre_process_time;
+
+ while (todo >= 0) {
+ _particles_process(frame_time);
+ todo -= frame_time;
+ }
+ }
+
+ if (fixed_fps > 0) {
+ float frame_time = 1.0 / fixed_fps;
+ float decr = frame_time;
+
+ float ldelta = delta;
+ if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
+ ldelta = 0.1;
+ } else if (ldelta <= 0.0) { //unlikely but..
+ ldelta = 0.001;
+ }
+ float todo = frame_remainder + ldelta;
+
+ while (todo >= frame_time) {
+ _particles_process(frame_time);
+ todo -= decr;
+ }
+
+ frame_remainder = todo;
+
+ } else {
+ _particles_process(delta);
+ }
+
+ _update_particle_data_buffer();
+}
+
void CPUParticles2D::_particles_process(float p_delta) {
p_delta *= speed_scale;
@@ -533,7 +622,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
time = Math::fmod(time, lifetime);
cycle++;
if (one_shot && cycle > 0) {
- emitting = false;
+ set_emitting(false);
+ _change_notify();
}
}
@@ -545,6 +635,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
velocity_xform[2] = Vector2();
}
+ float system_phase = time / lifetime;
+
for (int i = 0; i < pcount; i++) {
Particle &p = parray[i];
@@ -552,21 +644,26 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (!emitting && !p.active)
continue;
- float restart_time = (float(i) / float(pcount)) * lifetime;
float local_delta = p_delta;
+ // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle.
+ // While we use time in tests later on, for randomness we use the phase as done in the
+ // original shader code, and we later multiply by lifetime to get the time.
+ float restart_phase = float(i) / float(pcount);
+
if (randomness_ratio > 0.0) {
uint32_t seed = cycle;
- if (restart_time >= time) {
+ if (restart_phase >= system_phase) {
seed -= uint32_t(1);
}
seed *= uint32_t(pcount);
seed += uint32_t(i);
float random = float(idhash(seed) % uint32_t(65536)) / 65536.0;
- restart_time += randomness_ratio * random * 1.0 / float(pcount);
+ restart_phase += randomness_ratio * random * 1.0 / float(pcount);
}
- restart_time *= (1.0 - explosiveness_ratio);
+ restart_phase *= (1.0 - explosiveness_ratio);
+ float restart_time = restart_phase * lifetime;
bool restart = false;
if (time > prev_time) {
@@ -594,6 +691,10 @@ void CPUParticles2D::_particles_process(float p_delta) {
}
}
+ if (p.time * (1.0 - explosiveness_ratio) > p.lifetime) {
+ restart = true;
+ }
+
if (restart) {
if (!emitting) {
@@ -624,7 +725,7 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.hue_rot_rand = Math::randf();
p.anim_offset_rand = Math::randf();
- float angle1_rad = (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
+ float angle1_rad = Math::atan2(direction.y, direction.x) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0;
Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad));
p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]);
@@ -637,14 +738,17 @@ void CPUParticles2D::_particles_process(float p_delta) {
p.custom[3] = 0.0;
p.transform = Transform2D();
p.time = 0;
+ p.lifetime = lifetime * (1.0 - Math::randf() * lifetime_randomness);
p.base_color = Color(1, 1, 1, 1);
switch (emission_shape) {
case EMISSION_SHAPE_POINT: {
//do none
} break;
- case EMISSION_SHAPE_CIRCLE: {
- p.transform[2] = Vector2(Math::randf() * 2.0 - 1.0, Math::randf() * 2.0 - 1.0).normalized() * emission_sphere_radius;
+ case EMISSION_SHAPE_SPHERE: {
+ float s = Math::randf(), t = 2.0 * Math_PI * Math::randf();
+ float 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;
@@ -677,6 +781,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
} else if (!p.active) {
continue;
+ } else if (p.time > p.lifetime) {
+ p.active = false;
} else {
uint32_t alt_seed = p.seed;
@@ -688,16 +794,12 @@ void CPUParticles2D::_particles_process(float p_delta) {
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]);
}
- /*
- float tex_orbit_velocity = 0.0;
-
- if (flags[FLAG_DISABLE_Z]) {
- if (curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY].is_valid()) {
- tex_orbit_velocity = curve_parameters[PARAM_INITIAL_ORBIT_VELOCITY]->interpolate(p.custom[1]);
- }
+ float tex_orbit_velocity = 0.0;
+ if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) {
+ tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]);
}
-*/
+
float tex_angular_velocity = 0.0;
if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) {
tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]);
@@ -748,22 +850,19 @@ void CPUParticles2D::_particles_process(float p_delta) {
force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector2();
//apply tangential acceleration;
Vector2 yx = Vector2(diff.y, diff.x);
- force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
+ force += yx.length() > 0.0 ? (yx * Vector2(-1.0, 1.0)).normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector2();
//apply attractor forces
p.velocity += force * local_delta;
//orbit velocity
-#if 0
- if (flags[FLAG_DISABLE_Z]) {
-
- float orbit_amount = (orbit_velocity + tex_orbit_velocity) * mix(1.0, rand_from_seed(alt_seed), orbit_velocity_random);
- if (orbit_amount != 0.0) {
- float ang = orbit_amount * DELTA * pi * 2.0;
- mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));
- TRANSFORM[3].xy -= diff.xy;
- TRANSFORM[3].xy += rot * diff.xy;
- }
+ float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]);
+ if (orbit_amount != 0.0) {
+ float ang = orbit_amount * local_delta * Math_PI * 2.0;
+ // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix,
+ // but we use -ang here to reproduce its behavior.
+ Transform2D rot = Transform2D(-ang, Vector2());
+ p.transform[2] -= diff;
+ p.transform[2] += rot.basis_xform(diff);
}
-#endif
if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
p.velocity = p.velocity.normalized() * tex_linear_velocity;
}
@@ -839,8 +938,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
}
//scale by scale
- float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
- if (base_scale == 0.0) base_scale = 0.000001;
+ float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
+ if (base_scale < 0.000001) base_scale = 0.000001;
p.transform.elements[0] *= base_scale;
p.transform.elements[1] *= base_scale;
@@ -865,11 +964,6 @@ void CPUParticles2D::_update_particle_data_buffer() {
PoolVector<Particle>::Read r = particles.read();
float *ptr = w.ptr();
- Transform2D un_transform;
- if (!local_coords) {
- un_transform = get_global_transform().affine_inverse();
- }
-
if (draw_order != DRAW_ORDER_INDEX) {
ow = particle_order.write();
order = ow.ptr();
@@ -891,7 +985,7 @@ void CPUParticles2D::_update_particle_data_buffer() {
Transform2D t = r[idx].transform;
if (!local_coords) {
- t = un_transform * t;
+ t = inv_emission_transform * t;
}
if (r[idx].active) {
@@ -940,9 +1034,15 @@ void CPUParticles2D::_set_redraw(bool p_redraw) {
if (redraw) {
VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread");
VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), true);
+
+ VS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
} else {
- VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ if (VS::get_singleton()->is_connected("frame_pre_draw", this, "_update_render_thread")) {
+ VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread");
+ }
VS::get_singleton()->canvas_item_set_update_when_visible(get_canvas_item(), false);
+
+ VS::get_singleton()->multimesh_set_visible_instances(multimesh, 0);
}
#ifndef NO_THREADS
update_mutex->unlock();
@@ -973,12 +1073,13 @@ void CPUParticles2D::_notification(int p_what) {
_set_redraw(false);
}
- if (p_what == NOTIFICATION_PAUSED || p_what == NOTIFICATION_UNPAUSED) {
- }
-
if (p_what == NOTIFICATION_DRAW) {
+ // first update before rendering to avoid one frame delay after emitting starts
+ if (emitting && (time == 0))
+ _update_internal();
+
if (!redraw)
- return; // dont add to render list
+ return; // don't add to render list
RID texrid;
if (texture.is_valid()) {
@@ -994,77 +1095,50 @@ void CPUParticles2D::_notification(int p_what) {
}
if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+ _update_internal();
+ }
- if (particles.size() == 0 || !is_visible_in_tree()) {
- _set_redraw(false);
- return;
- }
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- float delta = get_process_delta_time();
- if (emitting) {
- inactive_time = 0;
- } else {
- inactive_time += delta;
- if (inactive_time > lifetime * 1.2) {
- set_process_internal(false);
- _set_redraw(false);
-
- //reset variables
- time = 0;
- inactive_time = 0;
- frame_remainder = 0;
- cycle = 0;
- return;
- }
- }
- _set_redraw(true);
+ inv_emission_transform = get_global_transform().affine_inverse();
- if (time == 0 && pre_process_time > 0.0) {
+ if (!local_coords) {
- float frame_time;
- if (fixed_fps > 0)
- frame_time = 1.0 / fixed_fps;
- else
- frame_time = 1.0 / 30.0;
+ int pc = particles.size();
- float todo = pre_process_time;
+ PoolVector<float>::Write w = particle_data.write();
+ PoolVector<Particle>::Read r = particles.read();
+ float *ptr = w.ptr();
- while (todo >= 0) {
- _particles_process(frame_time);
- todo -= frame_time;
- }
- }
+ for (int i = 0; i < pc; i++) {
- if (fixed_fps > 0) {
- float frame_time = 1.0 / fixed_fps;
- float decr = frame_time;
+ Transform2D t = inv_emission_transform * r[i].transform;
- float ldelta = delta;
- if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10
- ldelta = 0.1;
- } else if (ldelta <= 0.0) { //unlikely but..
- ldelta = 0.001;
- }
- float todo = frame_remainder + ldelta;
+ if (r[i].active) {
- while (todo >= frame_time) {
- _particles_process(frame_time);
- todo -= decr;
- }
+ ptr[0] = t.elements[0][0];
+ ptr[1] = t.elements[1][0];
+ ptr[2] = 0;
+ ptr[3] = t.elements[2][0];
+ ptr[4] = t.elements[0][1];
+ ptr[5] = t.elements[1][1];
+ ptr[6] = 0;
+ ptr[7] = t.elements[2][1];
- frame_remainder = todo;
+ } else {
+ zeromem(ptr, sizeof(float) * 8);
+ }
- } else {
- _particles_process(delta);
+ ptr += 13;
+ }
}
-
- _update_particle_data_buffer();
}
}
void CPUParticles2D::convert_from_particles(Node *p_particles) {
+
Particles2D *particles = Object::cast_to<Particles2D>(p_particles);
- ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND_MSG(!particles, "Only Particles2D nodes can be converted to CPUParticles2D.");
set_emitting(particles->is_emitting());
set_amount(particles->get_amount());
@@ -1089,6 +1163,8 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
if (material.is_null())
return;
+ Vector3 dir = material->get_direction();
+ set_direction(Vector2(dir.x, dir.y));
set_spread(material->get_spread());
set_flatness(material->get_flatness());
@@ -1108,6 +1184,7 @@ void CPUParticles2D::convert_from_particles(Node *p_particles) {
Vector2 gravity = Vector2(material->get_gravity().x, material->get_gravity().y);
set_gravity(gravity);
+ set_lifetime_randomness(material->get_lifetime_randomness());
#define CONVERT_PARAM(m_param) \
set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \
@@ -1142,6 +1219,7 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles2D::set_pre_process_time);
ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles2D::set_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles2D::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "random"), &CPUParticles2D::set_lifetime_randomness);
ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles2D::set_use_local_coordinates);
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);
@@ -1154,6 +1232,7 @@ void CPUParticles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles2D::get_pre_process_time);
ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles2D::get_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles2D::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &CPUParticles2D::get_lifetime_randomness);
ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles2D::get_use_local_coordinates);
ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles2D::get_fixed_fps);
ClassDB::bind_method(D_METHOD("get_fractional_delta"), &CPUParticles2D::get_fractional_delta);
@@ -1174,15 +1253,17 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "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"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Drawing", "");
+ // 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, "Texture"), "set_texture", "get_texture");
@@ -1193,6 +1274,9 @@ void CPUParticles2D::_bind_methods() {
////////////////////////////////
+ ClassDB::bind_method(D_METHOD("set_direction", "direction"), &CPUParticles2D::set_direction);
+ ClassDB::bind_method(D_METHOD("get_direction"), &CPUParticles2D::get_direction);
+
ClassDB::bind_method(D_METHOD("set_spread", "degrees"), &CPUParticles2D::set_spread);
ClassDB::bind_method(D_METHOD("get_spread"), &CPUParticles2D::get_spread);
@@ -1251,7 +1335,8 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors");
ADD_GROUP("Flags", "flag_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY);
- ADD_GROUP("Spread", "");
+ ADD_GROUP("Direction", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "direction"), "set_direction", "get_direction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
ADD_GROUP("Gravity", "");
@@ -1263,12 +1348,10 @@ void CPUParticles2D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY);
- /*
ADD_GROUP("Orbit Velocity", "orbit_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY);
-*/
ADD_GROUP("Linear Accel", "linear_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
@@ -1324,10 +1407,12 @@ void CPUParticles2D::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_MAX);
BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY);
+ BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); // Unused, but exposed for consistency with 3D.
+ BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); // Unused, but exposed for consistency with 3D.
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT);
- BIND_ENUM_CONSTANT(EMISSION_SHAPE_CIRCLE);
+ BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_RECTANGLE);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS);
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
@@ -1340,6 +1425,7 @@ CPUParticles2D::CPUParticles2D() {
frame_remainder = 0;
cycle = 0;
redraw = false;
+ emitting = false;
mesh = VisualServer::get_singleton()->mesh_create();
multimesh = VisualServer::get_singleton()->multimesh_create();
@@ -1354,17 +1440,19 @@ CPUParticles2D::CPUParticles2D() {
set_pre_process_time(0);
set_explosiveness_ratio(0);
set_randomness_ratio(0);
+ set_lifetime_randomness(0);
set_use_local_coordinates(true);
set_draw_order(DRAW_ORDER_INDEX);
set_speed_scale(1);
+ set_direction(Vector2(1, 0));
set_spread(45);
set_flatness(0);
- set_param(PARAM_INITIAL_LINEAR_VELOCITY, 1);
- //set_param(PARAM_ORBIT_VELOCITY, 0);
- set_param(PARAM_LINEAR_ACCEL, 0);
+ set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param(PARAM_ANGULAR_VELOCITY, 0);
+ set_param(PARAM_ORBIT_VELOCITY, 0);
+ set_param(PARAM_LINEAR_ACCEL, 0);
set_param(PARAM_RADIAL_ACCEL, 0);
set_param(PARAM_TANGENTIAL_ACCEL, 0);
set_param(PARAM_DAMPING, 0);
@@ -1377,7 +1465,7 @@ CPUParticles2D::CPUParticles2D() {
set_emission_sphere_radius(1);
set_emission_rect_extents(Vector2(1, 1));
- set_gravity(Vector2(0, 98.8));
+ set_gravity(Vector2(0, 98));
for (int i = 0; i < PARAM_MAX; i++) {
set_param_randomness(Parameter(i), 0);
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 23d2586331..47b4568dd4 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -35,10 +35,6 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/texture.h"
-/**
- @author Juan Linietsky <reduzio@gmail.com>
-*/
-
class CPUParticles2D : public Node2D {
private:
GDCLASS(CPUParticles2D, Node2D);
@@ -68,12 +64,14 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
+ FLAG_ROTATE_Y, // Unused, but exposed for consistency with 3D.
+ FLAG_DISABLE_Z, // Unused, but exposed for consistency with 3D.
FLAG_MAX
};
enum EmissionShape {
EMISSION_SHAPE_POINT,
- EMISSION_SHAPE_CIRCLE,
+ EMISSION_SHAPE_SPHERE,
EMISSION_SHAPE_RECTANGLE,
EMISSION_SHAPE_POINTS,
EMISSION_SHAPE_DIRECTED_POINTS,
@@ -94,6 +92,7 @@ private:
float hue_rot_rand;
float anim_offset_rand;
float time;
+ float lifetime;
Color base_color;
uint32_t seed;
@@ -116,7 +115,7 @@ private:
const Particle *particles;
bool operator()(int p_a, int p_b) const {
- return particles[p_a].time < particles[p_b].time;
+ return particles[p_a].time > particles[p_b].time;
}
};
@@ -137,11 +136,14 @@ private:
float pre_process_time;
float explosiveness_ratio;
float randomness_ratio;
+ float lifetime_randomness;
float speed_scale;
bool local_coords;
int fixed_fps;
bool fractional_delta;
+ Transform2D inv_emission_transform;
+
DrawOrder draw_order;
Ref<Texture> texture;
@@ -149,6 +151,7 @@ private:
////////
+ Vector2 direction;
float spread;
float flatness;
@@ -171,6 +174,7 @@ private:
Vector2 gravity;
+ void _update_internal();
void _particles_process(float p_delta);
void _update_particle_data_buffer();
@@ -195,6 +199,7 @@ public:
void set_pre_process_time(float p_time);
void set_explosiveness_ratio(float p_ratio);
void set_randomness_ratio(float p_ratio);
+ void set_lifetime_randomness(float p_random);
void set_visibility_aabb(const Rect2 &p_aabb);
void set_use_local_coordinates(bool p_enable);
void set_speed_scale(float p_scale);
@@ -206,6 +211,7 @@ public:
float get_pre_process_time() const;
float get_explosiveness_ratio() const;
float get_randomness_ratio() const;
+ float get_lifetime_randomness() const;
Rect2 get_visibility_aabb() const;
bool get_use_local_coordinates() const;
float get_speed_scale() const;
@@ -230,6 +236,9 @@ public:
///////////////////
+ void set_direction(Vector2 p_direction);
+ Vector2 get_direction() const;
+
void set_spread(float p_spread);
float get_spread() const;
@@ -248,7 +257,7 @@ public:
void set_color(const Color &p_color);
Color get_color() const;
- void set_color_ramp(const Ref<Gradient> &p_texture);
+ void set_color_ramp(const Ref<Gradient> &p_ramp);
Ref<Gradient> get_color_ramp() const;
void set_particle_flag(Flags p_flag, bool p_enable);
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
index 5b14b3e8e1..847d08b025 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joints_2d.cpp
@@ -37,8 +37,8 @@
void Joint2D::_update_joint(bool p_only_free) {
if (joint.is_valid()) {
- if (ba.is_valid() && bb.is_valid())
- Physics2DServer::get_singleton()->body_remove_collision_exception(ba, bb);
+ if (ba.is_valid() && bb.is_valid() && exclude_from_collision)
+ Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, false);
Physics2DServer::get_singleton()->free(joint);
joint = RID();
@@ -61,10 +61,6 @@ void Joint2D::_update_joint(bool p_only_free) {
if (!body_a || !body_b)
return;
- if (!body_a) {
- SWAP(body_a, body_b);
- }
-
joint = _configure_joint(body_a, body_b);
if (!joint.is_valid())
@@ -135,6 +131,8 @@ void Joint2D::set_exclude_nodes_from_collision(bool p_enable) {
if (exclude_from_collision == p_enable)
return;
+
+ _update_joint(true);
exclude_from_collision = p_enable;
_update_joint();
}
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index d1fce74085..7b3eab175a 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -347,7 +347,7 @@ void Light2D::_notification(int p_what) {
String Light2D::get_configuration_warning() const {
if (!texture.is_valid()) {
- return TTR("A texture with the shape of the light must be supplied to the 'texture' property.");
+ return TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.");
}
return String();
@@ -435,10 +435,10 @@ void Light2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix,Mask"), "set_mode", "get_mode");
ADD_GROUP("Range", "range_");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-100,100,0.1"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "range_height", PROPERTY_HINT_RANGE, "-2048,2048,0.1,or_lesser,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min");
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 5334caed67..313b23b9d4 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -32,9 +32,59 @@
#include "core/engine.h"
+#define LINE_GRAB_WIDTH 8
+Rect2 OccluderPolygon2D::_edit_get_rect() const {
+
+ if (rect_cache_dirty) {
+ if (closed) {
+ PoolVector<Vector2>::Read r = polygon.read();
+ item_rect = Rect2();
+ for (int i = 0; i < polygon.size(); i++) {
+ Vector2 pos = r[i];
+ if (i == 0)
+ item_rect.position = pos;
+ else
+ item_rect.expand_to(pos);
+ }
+ rect_cache_dirty = false;
+ } else {
+ if (polygon.size() == 0) {
+ item_rect = Rect2();
+ } else {
+ Vector2 d = Vector2(LINE_GRAB_WIDTH, LINE_GRAB_WIDTH);
+ item_rect = Rect2(polygon[0] - d, 2 * d);
+ for (int i = 1; i < polygon.size(); i++) {
+ item_rect.expand_to(polygon[i] - d);
+ item_rect.expand_to(polygon[i] + d);
+ }
+ }
+ }
+ }
+
+ return item_rect;
+}
+
+bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+
+ if (closed) {
+ return Geometry::is_point_in_polygon(p_point, Variant(polygon));
+ } else {
+ const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance;
+ PoolVector<Vector2>::Read points = polygon.read();
+ for (int i = 0; i < polygon.size() - 1; i++) {
+ Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]);
+ if (p.distance_to(p_point) <= d)
+ return true;
+ }
+
+ return false;
+ }
+}
+
void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) {
polygon = p_polygon;
+ rect_cache_dirty = true;
VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed);
emit_changed();
}
@@ -100,6 +150,7 @@ OccluderPolygon2D::OccluderPolygon2D() {
occ_polygon = VS::get_singleton()->canvas_occluder_polygon_create();
closed = true;
cull = CULL_DISABLED;
+ rect_cache_dirty = true;
}
OccluderPolygon2D::~OccluderPolygon2D() {
@@ -164,6 +215,16 @@ void LightOccluder2D::_notification(int p_what) {
}
}
+Rect2 LightOccluder2D::_edit_get_rect() const {
+
+ return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2();
+}
+
+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;
+}
+
void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) {
#ifdef DEBUG_ENABLED
@@ -207,7 +268,7 @@ String LightOccluder2D::get_configuration_warning() const {
}
if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size() == 0) {
- return TTR("The occluder polygon for this occluder is empty. Please draw a polygon!");
+ return TTR("The occluder polygon for this occluder is empty. Please draw a polygon.");
}
return String();
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 498d65d764..7931cd0b30 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -50,10 +50,16 @@ private:
bool closed;
CullMode cull;
+ mutable Rect2 item_rect;
+ mutable bool rect_cache_dirty;
+
protected:
static void _bind_methods();
public:
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
void set_polygon(const PoolVector<Vector2> &p_polygon);
PoolVector<Vector2> get_polygon() const;
@@ -85,6 +91,9 @@ protected:
static void _bind_methods();
public:
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon);
Ref<OccluderPolygon2D> get_occluder_polygon() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 73692e0535..af3ed671aa 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -38,15 +38,16 @@ VARIANT_ENUM_CAST(Line2D::LineJointMode)
VARIANT_ENUM_CAST(Line2D::LineCapMode)
VARIANT_ENUM_CAST(Line2D::LineTextureMode)
-Line2D::Line2D() :
- Node2D() {
+Line2D::Line2D() {
_joint_mode = LINE_JOINT_SHARP;
_begin_cap_mode = LINE_CAP_NONE;
_end_cap_mode = LINE_CAP_NONE;
_width = 10;
_default_color = Color(0.4, 0.5, 1);
+ _texture_mode = LINE_TEXTURE_NONE;
_sharp_limit = 2.f;
_round_precision = 8;
+ _antialiased = false;
}
Rect2 Line2D::_edit_get_rect() const {
@@ -84,10 +85,10 @@ void Line2D::set_points(const PoolVector<Vector2> &p_points) {
update();
}
-void Line2D::set_width(float width) {
- if (width < 0.0)
- width = 0.0;
- _width = width;
+void Line2D::set_width(float p_width) {
+ if (p_width < 0.0)
+ p_width = 0.0;
+ _width = p_width;
update();
}
@@ -95,16 +96,37 @@ float Line2D::get_width() const {
return _width;
}
+void Line2D::set_curve(const Ref<Curve> &p_curve) {
+ // Cleanup previous connection if any
+ if (_curve.is_valid()) {
+ _curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_changed");
+ }
+
+ _curve = p_curve;
+
+ // Connect to the curve so the line will update when it is changed
+ if (_curve.is_valid()) {
+ _curve->connect(CoreStringNames::get_singleton()->changed, this, "_curve_changed");
+ }
+
+ update();
+}
+
+Ref<Curve> Line2D::get_curve() const {
+ return _curve;
+}
+
PoolVector<Vector2> Line2D::get_points() const {
return _points;
}
-void Line2D::set_point_position(int i, Vector2 pos) {
- _points.set(i, pos);
+void Line2D::set_point_position(int i, Vector2 p_pos) {
+ _points.set(i, p_pos);
update();
}
Vector2 Line2D::get_point_position(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), Vector2());
return _points.get(i);
}
@@ -120,8 +142,12 @@ void Line2D::clear_points() {
}
}
-void Line2D::add_point(Vector2 pos) {
- _points.append(pos);
+void Line2D::add_point(Vector2 p_pos, int p_atpos) {
+ if (p_atpos < 0 || _points.size() < p_atpos) {
+ _points.append(p_pos);
+ } else {
+ _points.insert(p_atpos, p_pos);
+ }
update();
}
@@ -130,8 +156,8 @@ void Line2D::remove_point(int i) {
update();
}
-void Line2D::set_default_color(Color color) {
- _default_color = color;
+void Line2D::set_default_color(Color p_color) {
+ _default_color = p_color;
update();
}
@@ -139,18 +165,18 @@ Color Line2D::get_default_color() const {
return _default_color;
}
-void Line2D::set_gradient(const Ref<Gradient> &gradient) {
+void Line2D::set_gradient(const Ref<Gradient> &p_gradient) {
// Cleanup previous connection if any
if (_gradient.is_valid()) {
- (**_gradient).disconnect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
+ _gradient->disconnect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
- _gradient = gradient;
+ _gradient = p_gradient;
// Connect to the gradient so the line will update when the ColorRamp is changed
if (_gradient.is_valid()) {
- (**_gradient).connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
+ _gradient->connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
update();
@@ -160,8 +186,8 @@ Ref<Gradient> Line2D::get_gradient() const {
return _gradient;
}
-void Line2D::set_texture(const Ref<Texture> &texture) {
- _texture = texture;
+void Line2D::set_texture(const Ref<Texture> &p_texture) {
+ _texture = p_texture;
update();
}
@@ -169,8 +195,8 @@ Ref<Texture> Line2D::get_texture() const {
return _texture;
}
-void Line2D::set_texture_mode(const LineTextureMode mode) {
- _texture_mode = mode;
+void Line2D::set_texture_mode(const LineTextureMode p_mode) {
+ _texture_mode = p_mode;
update();
}
@@ -178,8 +204,8 @@ Line2D::LineTextureMode Line2D::get_texture_mode() const {
return _texture_mode;
}
-void Line2D::set_joint_mode(LineJointMode mode) {
- _joint_mode = mode;
+void Line2D::set_joint_mode(LineJointMode p_mode) {
+ _joint_mode = p_mode;
update();
}
@@ -187,8 +213,8 @@ Line2D::LineJointMode Line2D::get_joint_mode() const {
return _joint_mode;
}
-void Line2D::set_begin_cap_mode(LineCapMode mode) {
- _begin_cap_mode = mode;
+void Line2D::set_begin_cap_mode(LineCapMode p_mode) {
+ _begin_cap_mode = p_mode;
update();
}
@@ -196,8 +222,8 @@ Line2D::LineCapMode Line2D::get_begin_cap_mode() const {
return _begin_cap_mode;
}
-void Line2D::set_end_cap_mode(LineCapMode mode) {
- _end_cap_mode = mode;
+void Line2D::set_end_cap_mode(LineCapMode p_mode) {
+ _end_cap_mode = p_mode;
update();
}
@@ -213,10 +239,10 @@ void Line2D::_notification(int p_what) {
}
}
-void Line2D::set_sharp_limit(float limit) {
- if (limit < 0.f)
- limit = 0.f;
- _sharp_limit = limit;
+void Line2D::set_sharp_limit(float p_limit) {
+ if (p_limit < 0.f)
+ p_limit = 0.f;
+ _sharp_limit = p_limit;
update();
}
@@ -224,10 +250,10 @@ float Line2D::get_sharp_limit() const {
return _sharp_limit;
}
-void Line2D::set_round_precision(int precision) {
- if (precision < 1)
- precision = 1;
- _round_precision = precision;
+void Line2D::set_round_precision(int p_precision) {
+ if (p_precision < 1)
+ p_precision = 1;
+ _round_precision = p_precision;
update();
}
@@ -235,6 +261,15 @@ int Line2D::get_round_precision() const {
return _round_precision;
}
+void Line2D::set_antialiased(bool p_antialiased) {
+ _antialiased = p_antialiased;
+ update();
+}
+
+bool Line2D::get_antialiased() const {
+ return _antialiased;
+}
+
void Line2D::_draw() {
if (_points.size() <= 1 || _width == 0.f)
return;
@@ -263,10 +298,11 @@ void Line2D::_draw() {
lb.round_precision = _round_precision;
lb.sharp_limit = _sharp_limit;
lb.width = _width;
+ lb.curve = *_curve;
RID texture_rid;
if (_texture.is_valid()) {
- texture_rid = (**_texture).get_rid();
+ texture_rid = _texture->get_rid();
lb.tile_aspect = _texture->get_size().aspect();
}
@@ -279,8 +315,8 @@ void Line2D::_draw() {
lb.vertices,
lb.colors,
lb.uvs, Vector<int>(), Vector<float>(),
-
- texture_rid);
+ texture_rid, -1, RID(),
+ _antialiased);
// DEBUG
// Draw wireframe
@@ -307,6 +343,10 @@ void Line2D::_gradient_changed() {
update();
}
+void Line2D::_curve_changed() {
+ update();
+}
+
// static
void Line2D::_bind_methods() {
@@ -318,7 +358,7 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Line2D::get_point_count);
- ClassDB::bind_method(D_METHOD("add_point", "position"), &Line2D::add_point);
+ ClassDB::bind_method(D_METHOD("add_point", "position", "at_position"), &Line2D::add_point, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("remove_point", "i"), &Line2D::remove_point);
ClassDB::bind_method(D_METHOD("clear_points"), &Line2D::clear_points);
@@ -326,6 +366,9 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &Line2D::get_width);
+ ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Line2D::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve"), &Line2D::get_curve);
+
ClassDB::bind_method(D_METHOD("set_default_color", "color"), &Line2D::set_default_color);
ClassDB::bind_method(D_METHOD("get_default_color"), &Line2D::get_default_color);
@@ -353,8 +396,12 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_round_precision", "precision"), &Line2D::set_round_precision);
ClassDB::bind_method(D_METHOD("get_round_precision"), &Line2D::get_round_precision);
+ ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &Line2D::set_antialiased);
+ ClassDB::bind_method(D_METHOD("get_antialiased"), &Line2D::get_antialiased);
+
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "width"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color");
ADD_GROUP("Fill", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
@@ -367,6 +414,7 @@ void Line2D::_bind_methods() {
ADD_GROUP("Border", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "sharp_limit"), "set_sharp_limit", "get_sharp_limit");
ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision"), "set_round_precision", "get_round_precision");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased"), "set_antialiased", "get_antialiased");
BIND_ENUM_CONSTANT(LINE_JOINT_SHARP);
BIND_ENUM_CONSTANT(LINE_JOINT_BEVEL);
@@ -381,4 +429,5 @@ void Line2D::_bind_methods() {
BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH);
ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed);
+ ClassDB::bind_method(D_METHOD("_curve_changed"), &Line2D::_curve_changed);
}
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 32befab2d3..10b2bf16e4 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -35,7 +35,7 @@
class Line2D : public Node2D {
- GDCLASS(Line2D, Node2D)
+ GDCLASS(Line2D, Node2D);
public:
enum LineJointMode {
@@ -72,12 +72,15 @@ public:
void clear_points();
- void add_point(Vector2 pos);
+ void add_point(Vector2 pos, int atpos = -1);
void remove_point(int i);
void set_width(float width);
float get_width() const;
+ void set_curve(const Ref<Curve> &curve);
+ Ref<Curve> get_curve() const;
+
void set_default_color(Color color);
Color get_default_color() const;
@@ -105,6 +108,9 @@ public:
void set_round_precision(int precision);
int get_round_precision() const;
+ void set_antialiased(bool p_antialiased);
+ bool get_antialiased() const;
+
protected:
void _notification(int p_what);
void _draw();
@@ -113,6 +119,7 @@ protected:
private:
void _gradient_changed();
+ void _curve_changed();
private:
PoolVector<Vector2> _points;
@@ -120,12 +127,14 @@ private:
LineCapMode _begin_cap_mode;
LineCapMode _end_cap_mode;
float _width;
+ Ref<Curve> _curve;
Color _default_color;
Ref<Gradient> _gradient;
Ref<Texture> _texture;
LineTextureMode _texture_mode;
float _sharp_limit;
int _round_precision;
+ bool _antialiased;
};
#endif // LINE2D_H
diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp
index eb09d3c9d3..6436b3878f 100644
--- a/scene/2d/line_builder.cpp
+++ b/scene/2d/line_builder.cpp
@@ -95,6 +95,7 @@ static inline Vector2 interpolate(const Rect2 &r, const Vector2 &v) {
LineBuilder::LineBuilder() {
joint_mode = Line2D::LINE_JOINT_SHARP;
width = 10;
+ curve = NULL;
default_color = Color(0.4, 0.5, 1);
gradient = NULL;
sharp_limit = 2.f;
@@ -136,8 +137,8 @@ void LineBuilder::build() {
Vector2 pos1 = points[1];
Vector2 f0 = (pos1 - pos0).normalized();
Vector2 u0 = rotate90(f0);
- Vector2 pos_up0 = pos0 + u0 * hw;
- Vector2 pos_down0 = pos0 - u0 * hw;
+ Vector2 pos_up0 = pos0;
+ Vector2 pos_down0 = pos0;
Color color0;
Color color1;
@@ -145,12 +146,30 @@ void LineBuilder::build() {
float current_distance0 = 0.f;
float current_distance1 = 0.f;
float total_distance = 0.f;
+ float width_factor = 1.f;
_interpolate_color = gradient != NULL;
+ bool retrieve_curve = curve != NULL;
bool distance_required = _interpolate_color ||
+ retrieve_curve ||
texture_mode == Line2D::LINE_TEXTURE_TILE ||
texture_mode == Line2D::LINE_TEXTURE_STRETCH;
- if (distance_required)
+ if (distance_required) {
total_distance = calculate_total_distance(points);
+ //Adjust totalDistance.
+ // The line's outer length will be a little higher due to begin and end caps
+ if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) {
+ if (retrieve_curve)
+ total_distance += width * curve->interpolate_baked(0.f) * 0.5f;
+ else
+ total_distance += width * 0.5f;
+ }
+ if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) {
+ if (retrieve_curve)
+ total_distance += width * curve->interpolate_baked(1.f) * 0.5f;
+ else
+ total_distance += width * 0.5f;
+ }
+ }
if (_interpolate_color)
color0 = gradient->get_color(0);
else
@@ -159,22 +178,28 @@ void LineBuilder::build() {
float uvx0 = 0.f;
float uvx1 = 0.f;
+ if (retrieve_curve)
+ width_factor = curve->interpolate_baked(0.f);
+
+ pos_up0 += u0 * hw * width_factor;
+ pos_down0 -= u0 * hw * width_factor;
+
// Begin cap
if (begin_cap_mode == Line2D::LINE_CAP_BOX) {
// Push back first vertices a little bit
- pos_up0 -= f0 * hw;
- pos_down0 -= f0 * hw;
- // The line's outer length will be a little higher due to begin and end caps
- total_distance += width;
- current_distance0 += hw;
+ pos_up0 -= f0 * hw * width_factor;
+ pos_down0 -= f0 * hw * width_factor;
+
+ current_distance0 += hw * width_factor;
current_distance1 = current_distance0;
} else if (begin_cap_mode == Line2D::LINE_CAP_ROUND) {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
- uvx0 = 0.5f / tile_aspect;
+ uvx0 = width_factor * 0.5f / tile_aspect;
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ uvx0 = width * width_factor / total_distance;
}
- new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f));
- total_distance += width;
- current_distance0 += hw;
+ new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, uvx0 * 2, 1.f));
+ current_distance0 += hw * width_factor;
current_distance1 = current_distance0;
}
@@ -206,13 +231,23 @@ void LineBuilder::build() {
const float dp = u0.dot(f1);
const Orientation orientation = (dp > 0.f ? UP : DOWN);
+ if (distance_required) {
+ current_distance1 += pos0.distance_to(pos1);
+ }
+ if (_interpolate_color) {
+ color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
+ }
+ if (retrieve_curve) {
+ width_factor = curve->interpolate_baked(current_distance1 / total_distance);
+ }
+
Vector2 inner_normal0, inner_normal1;
if (orientation == UP) {
- inner_normal0 = u0 * hw;
- inner_normal1 = u1 * hw;
+ inner_normal0 = u0 * hw * width_factor;
+ inner_normal1 = u1 * hw * width_factor;
} else {
- inner_normal0 = -u0 * hw;
- inner_normal1 = -u1 * hw;
+ inner_normal0 = -u0 * hw * width_factor;
+ inner_normal1 = -u1 * hw * width_factor;
}
/*
@@ -259,7 +294,8 @@ void LineBuilder::build() {
Vector2 pos_up1, pos_down1;
if (intersection_result == SEGMENT_INTERSECT) {
// Fallback on bevel if sharp angle is too high (because it would produce very long miters)
- if (current_joint_mode == Line2D::LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / hw_sq > sharp_limit_sq) {
+ float width_factor_sq = width_factor * width_factor;
+ if (current_joint_mode == Line2D::LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / (hw_sq * width_factor_sq) > sharp_limit_sq) {
current_joint_mode = Line2D::LINE_JOINT_BEVEL;
}
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
@@ -271,9 +307,9 @@ void LineBuilder::build() {
// Bevel or round
if (orientation == UP) {
pos_up1 = corner_pos_up;
- pos_down1 = pos1 - u0 * hw;
+ pos_down1 = pos1 - u0 * hw * width_factor;
} else {
- pos_up1 = pos1 + u0 * hw;
+ pos_up1 = pos1 + u0 * hw * width_factor;
pos_down1 = corner_pos_down;
}
}
@@ -289,12 +325,6 @@ void LineBuilder::build() {
// Add current line body quad
// Triangles are clockwise
- if (distance_required) {
- current_distance1 += pos0.distance_to(pos1);
- }
- if (_interpolate_color) {
- color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
- }
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
@@ -315,15 +345,15 @@ void LineBuilder::build() {
} else {
if (orientation == UP) {
pos_up0 = corner_pos_up;
- pos_down0 = pos1 - u1 * hw;
+ pos_down0 = pos1 - u1 * hw * width_factor;
} else {
- pos_up0 = pos1 + u1 * hw;
+ pos_up0 = pos1 + u1 * hw * width_factor;
pos_down0 = corner_pos_down;
}
}
} else {
- pos_up0 = pos1 + u1 * hw;
- pos_down0 = pos1 - u1 * hw;
+ pos_up0 = pos1 + u1 * hw * width_factor;
+ pos_down0 = pos1 - u1 * hw * width_factor;
}
// From this point, bu0 and bd0 concern the next segment
@@ -362,26 +392,28 @@ void LineBuilder::build() {
strip_begin(pos_up0, pos_down0, color1, uvx1);
}
}
-
// Last (or only) segment
-
pos1 = points[points.size() - 1];
- Vector2 pos_up1 = pos1 + u0 * hw;
- Vector2 pos_down1 = pos1 - u0 * hw;
-
- // End cap (box)
- if (end_cap_mode == Line2D::LINE_CAP_BOX) {
- pos_up1 += f0 * hw;
- pos_down1 += f0 * hw;
- }
-
if (distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if (_interpolate_color) {
color1 = gradient->get_color(gradient->get_points_count() - 1);
}
+ if (retrieve_curve) {
+ width_factor = curve->interpolate_baked(1.f);
+ }
+
+ Vector2 pos_up1 = pos1 + u0 * hw * width_factor;
+ Vector2 pos_down1 = pos1 - u0 * hw * width_factor;
+
+ // End cap (box)
+ if (end_cap_mode == Line2D::LINE_CAP_BOX) {
+ pos_up1 += f0 * hw * width_factor;
+ pos_down1 += f0 * hw * width_factor;
+ }
+
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
@@ -394,7 +426,13 @@ void LineBuilder::build() {
if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
// Note: color is not used in case we don't interpolate...
Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
- new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f));
+ float dist = 0;
+ if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
+ dist = width_factor / tile_aspect;
+ } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
+ dist = width * width_factor / total_distance;
+ }
+ new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f * dist, 0.f, dist, 1.f));
}
}
diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h
index b961385e33..91b4518f9b 100644
--- a/scene/2d/line_builder.h
+++ b/scene/2d/line_builder.h
@@ -45,6 +45,7 @@ public:
Line2D::LineCapMode begin_cap_mode;
Line2D::LineCapMode end_cap_mode;
float width;
+ Curve *curve;
Color default_color;
Gradient *gradient;
Line2D::LineTextureMode texture_mode;
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index b382ca7b33..bcd4bca940 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -50,6 +50,8 @@ void MeshInstance2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map);
ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index 4d81c8088a..af552415ca 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -34,7 +34,7 @@
#include "scene/2d/node_2d.h"
class MeshInstance2D : public Node2D {
- GDCLASS(MeshInstance2D, Node2D)
+ GDCLASS(MeshInstance2D, Node2D);
Ref<Mesh> mesh;
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
new file mode 100644
index 0000000000..ca75302163
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* multimesh_instance_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multimesh_instance_2d.h"
+
+void MultiMeshInstance2D::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+ if (multimesh.is_valid()) {
+ draw_multimesh(multimesh, texture, normal_map);
+ }
+ }
+}
+
+void MultiMeshInstance2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_multimesh", "multimesh"), &MultiMeshInstance2D::set_multimesh);
+ ClassDB::bind_method(D_METHOD("get_multimesh"), &MultiMeshInstance2D::get_multimesh);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MultiMeshInstance2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &MultiMeshInstance2D::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MultiMeshInstance2D::set_normal_map);
+ ClassDB::bind_method(D_METHOD("get_normal_map"), &MultiMeshInstance2D::get_normal_map);
+
+ ADD_SIGNAL(MethodInfo("texture_changed"));
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multimesh", PROPERTY_HINT_RESOURCE_TYPE, "MultiMesh"), "set_multimesh", "get_multimesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+}
+
+void MultiMeshInstance2D::set_multimesh(const Ref<MultiMesh> &p_multimesh) {
+
+ multimesh = p_multimesh;
+ update();
+}
+
+Ref<MultiMesh> MultiMeshInstance2D::get_multimesh() const {
+
+ return multimesh;
+}
+
+void MultiMeshInstance2D::set_texture(const Ref<Texture> &p_texture) {
+
+ if (p_texture == texture)
+ return;
+ texture = p_texture;
+ update();
+ emit_signal("texture_changed");
+ _change_notify("texture");
+}
+
+Ref<Texture> MultiMeshInstance2D::get_texture() const {
+
+ return texture;
+}
+
+void MultiMeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) {
+
+ normal_map = p_texture;
+ update();
+}
+
+Ref<Texture> MultiMeshInstance2D::get_normal_map() const {
+
+ return normal_map;
+}
+
+Rect2 MultiMeshInstance2D::_edit_get_rect() const {
+
+ if (multimesh.is_valid()) {
+ AABB aabb = multimesh->get_aabb();
+ return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+ }
+
+ return Node2D::_edit_get_rect();
+}
+
+MultiMeshInstance2D::MultiMeshInstance2D() {
+}
+
+MultiMeshInstance2D::~MultiMeshInstance2D() {
+}
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
new file mode 100644
index 0000000000..3795497183
--- /dev/null
+++ b/scene/2d/multimesh_instance_2d.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* multimesh_instance_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIMESH_INSTANCE_2D_H
+#define MULTIMESH_INSTANCE_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/multimesh.h"
+
+class MultiMeshInstance2D : public Node2D {
+ GDCLASS(MultiMeshInstance2D, Node2D);
+
+ Ref<MultiMesh> multimesh;
+
+ Ref<Texture> texture;
+ Ref<Texture> normal_map;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_multimesh(const Ref<MultiMesh> &p_multimesh);
+ Ref<MultiMesh> get_multimesh() const;
+
+ void set_texture(const Ref<Texture> &p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_normal_map(const Ref<Texture> &p_texture);
+ Ref<Texture> get_normal_map() const;
+
+ virtual Rect2 _edit_get_rect() const;
+
+ MultiMeshInstance2D();
+ ~MultiMeshInstance2D();
+};
+
+#endif // MULTIMESH_INSTANCE_2D_H
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index 57e0a5b118..c7d2dabbae 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -92,7 +92,6 @@ void Navigation2D::_navpoly_link(int p_id) {
if (!valid) {
nm.polygons.pop_back();
ERR_CONTINUE(!valid);
- continue;
}
p.center = center / plen;
@@ -542,7 +541,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) {
//process
- if (portal_left.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
+ if (portal_left.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) {
left_poly = p;
portal_left = left;
} else {
@@ -552,7 +551,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
left_poly = p;
portal_left = apex_point;
portal_right = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
path.push_back(apex_point);
skip = true;
}
@@ -560,7 +559,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) {
//process
- if (portal_right.distance_squared_to(apex_point) < CMP_EPSILON || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
+ if (portal_right.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) {
right_poly = p;
portal_right = right;
} else {
@@ -570,7 +569,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
right_poly = p;
portal_right = apex_point;
portal_left = apex_point;
- if (!path.size() || path[path.size() - 1].distance_to(apex_point) > CMP_EPSILON)
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point))
path.push_back(apex_point);
}
}
@@ -596,7 +595,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
}
}
- if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) {
+ if (!path.size() || !path[path.size() - 1].is_equal_approx(begin_point)) {
path.push_back(begin_point); // Add the begin point
} else {
path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point
@@ -604,7 +603,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
path.invert();
- if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) {
+ if (path.size() <= 1 || !path[path.size() - 1].is_equal_approx(end_point)) {
path.push_back(end_point); // Add the end point
} else {
path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index 0f6af358bd..678db85ff0 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -271,7 +271,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
struct Polygon p;
- for (int i = 0; i < tp.GetNumPoints(); i++) {
+ for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
Map<Vector2, int>::Element *E = points.find(tp[i]);
if (!E) {
@@ -312,7 +312,7 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines);
ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines);
- ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
}
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index f4430d93f6..336c8aa78e 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -437,7 +437,7 @@ void Node2D::_bind_methods() {
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_rotation", "get_rotation");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", 0), "set_transform", "get_transform");
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index baf5b5967b..0823e09110 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -69,6 +69,9 @@ Size2 ParallaxLayer::get_motion_offset() const {
void ParallaxLayer::_update_mirroring() {
+ if (!is_inside_tree())
+ return;
+
ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent());
if (pb) {
@@ -105,6 +108,11 @@ void ParallaxLayer::_notification(int p_what) {
orig_scale = get_scale();
_update_mirroring();
} break;
+ case NOTIFICATION_EXIT_TREE: {
+
+ set_position(orig_offset);
+ set_scale(orig_scale);
+ } break;
}
}
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 9701998f5d..0bf8237d37 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -41,17 +41,23 @@
void Particles2D::set_emitting(bool p_emitting) {
VS::get_singleton()->particles_set_emitting(particles, p_emitting);
+
+ if (p_emitting && one_shot) {
+ set_process_internal(true);
+ } else if (!p_emitting) {
+ set_process_internal(false);
+ }
}
void Particles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles cannot be smaller than 1.");
amount = p_amount;
VS::get_singleton()->particles_set_amount(particles, amount);
}
void Particles2D::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
@@ -60,8 +66,16 @@ void Particles2D::set_one_shot(bool p_enable) {
one_shot = p_enable;
VS::get_singleton()->particles_set_one_shot(particles, one_shot);
- if (!one_shot && is_emitting())
- VisualServer::get_singleton()->particles_restart(particles);
+
+ if (is_emitting()) {
+
+ set_process_internal(true);
+ if (!one_shot)
+ VisualServer::get_singleton()->particles_restart(particles);
+ }
+
+ if (!one_shot)
+ set_process_internal(false);
}
void Particles2D::set_pre_process_time(float p_time) {
@@ -278,6 +292,7 @@ void Particles2D::_validate_property(PropertyInfo &property) const {
void Particles2D::restart() {
VS::get_singleton()->particles_restart(particles);
+ VS::get_singleton()->particles_set_emitting(particles, true);
}
void Particles2D::_notification(int p_what) {
@@ -313,6 +328,14 @@ void Particles2D::_notification(int p_what) {
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
_update_particle_emission_transform();
}
+
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+
+ if (one_shot && !is_emitting()) {
+ _change_notify();
+ set_process_internal(false);
+ }
+ }
}
void Particles2D::_bind_methods() {
@@ -387,6 +410,7 @@ Particles2D::Particles2D() {
particles = VS::get_singleton()->particles_create();
+ one_shot = false; // Needed so that set_emitting doesn't access uninitialized values
set_emitting(true);
set_one_shot(false);
set_amount(8);
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
index a0104b4b16..0276978f83 100644
--- a/scene/2d/particles_2d.h
+++ b/scene/2d/particles_2d.h
@@ -37,7 +37,7 @@
class Particles2D : public Node2D {
private:
- GDCLASS(Particles2D, Node2D)
+ GDCLASS(Particles2D, Node2D);
public:
enum DrawOrder {
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index fec861ad2f..18ace5892a 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -58,7 +58,7 @@ Rect2 Path2D::_edit_get_rect() const {
}
bool Path2D::_edit_use_rect() const {
- return true;
+ return curve.is_valid() && curve->get_point_count() != 0;
}
bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
@@ -95,7 +95,7 @@ void Path2D::_notification(int p_what) {
return;
}
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
const float line_width = 2 * EDSCALE;
#else
const float line_width = 2;
@@ -110,7 +110,7 @@ void Path2D::_notification(int p_what) {
real_t frac = j / 8.0;
Vector2 p = curve->interpolate(i, frac);
- draw_line(prev_p, p, color, line_width);
+ draw_line(prev_p, p, color, line_width, true);
prev_p = p;
}
}
@@ -173,16 +173,10 @@ void PathFollow2D::_update_transform() {
if (path_length == 0) {
return;
}
- float bounded_offset = offset;
- if (loop)
- bounded_offset = Math::fposmod(bounded_offset, path_length);
- else
- bounded_offset = CLAMP(bounded_offset, 0, path_length);
-
- Vector2 pos = c->interpolate_baked(bounded_offset, cubic);
+ Vector2 pos = c->interpolate_baked(offset, cubic);
if (rotate) {
- float ahead = bounded_offset + lookahead;
+ float ahead = offset + lookahead;
if (loop && ahead >= path_length) {
// If our lookahead will loop, we need to check if the path is closed.
@@ -206,7 +200,7 @@ void PathFollow2D::_update_transform() {
// This will happen at the end of non-looping or non-closed paths.
// We'll try a look behind instead, in order to get a meaningful angle.
tangent_to_curve =
- (pos - c->interpolate_baked(bounded_offset - lookahead, cubic)).normalized();
+ (pos - c->interpolate_baked(offset - lookahead, cubic)).normalized();
} else {
tangent_to_curve = (ahead_pos - pos).normalized();
}
@@ -264,7 +258,7 @@ void PathFollow2D::_validate_property(PropertyInfo &property) const {
if (path && path->get_curve().is_valid())
max = path->get_curve()->get_baked_length();
- property.hint_string = "0," + rtos(max) + ",0.01,or_greater";
+ property.hint_string = "0," + rtos(max) + ",0.01,or_lesser";
}
}
@@ -306,8 +300,8 @@ void PathFollow2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);
ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01,or_lesser"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001,or_lesser", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotate"), "set_rotate", "is_rotating");
@@ -319,8 +313,24 @@ void PathFollow2D::_bind_methods() {
void PathFollow2D::set_offset(float p_offset) {
offset = p_offset;
- if (path)
+ if (path) {
+ if (path->get_curve().is_valid() && path->get_curve()->get_baked_length()) {
+ float path_length = path->get_curve()->get_baked_length();
+
+ if (loop) {
+ while (offset > path_length)
+ offset -= path_length;
+
+ while (offset < 0)
+ offset += path_length;
+
+ } else {
+ offset = CLAMP(offset, 0, path_length);
+ }
+ }
+
_update_transform();
+ }
_change_notify("offset");
_change_notify("unit_offset");
}
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 690f1d4b4a..a57bfd4cbe 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -157,10 +157,7 @@ void PhysicsBody2D::add_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
- }
- ERR_FAIL_COND(!physics_body);
+ ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two objects of PhysicsBody type.");
Physics2DServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid());
}
@@ -168,10 +165,7 @@ void PhysicsBody2D::remove_collision_exception_with(Node *p_node) {
ERR_FAIL_NULL(p_node);
PhysicsBody2D *physics_body = Object::cast_to<PhysicsBody2D>(p_node);
- if (!physics_body) {
- ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type");
- }
- ERR_FAIL_COND(!physics_body);
+ ERR_FAIL_COND_MSG(!physics_body, "Collision exception only works between two objects of PhysicsBody type.");
Physics2DServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid());
}
@@ -203,10 +197,9 @@ void StaticBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+ ERR_FAIL_COND_MSG(p_friction < 0 || p_friction > 1, "Friction must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -217,8 +210,7 @@ void StaticBody2D::set_friction(real_t p_friction) {
real_t StaticBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
if (physics_material_override.is_null()) {
return 1;
@@ -233,10 +225,9 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+ ERR_FAIL_COND_MSG(p_bounce < 0 || p_bounce > 1, "Bounce must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -247,8 +238,7 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
real_t StaticBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
if (physics_material_override.is_null()) {
return 0;
@@ -604,7 +594,7 @@ real_t RigidBody2D::get_mass() const {
void RigidBody2D::set_inertia(real_t p_inertia) {
- ERR_FAIL_COND(p_inertia <= 0);
+ ERR_FAIL_COND(p_inertia < 0);
Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_INERTIA, p_inertia);
}
@@ -630,10 +620,9 @@ void RigidBody2D::set_friction(real_t p_friction) {
return;
}
- ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+ ERR_FAIL_COND_MSG(p_friction < 0 || p_friction > 1, "Friction must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -643,8 +632,7 @@ void RigidBody2D::set_friction(real_t p_friction) {
}
real_t RigidBody2D::get_friction() const {
- ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method get_friction has been deprecated and will be removed in the future, use physics material instead.");
if (physics_material_override.is_null()) {
return 1;
@@ -659,8 +647,7 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
return;
}
- ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
@@ -672,8 +659,7 @@ void RigidBody2D::set_bounce(real_t p_bounce) {
}
real_t RigidBody2D::get_bounce() const {
- ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.")
- WARN_DEPRECATED
+ WARN_DEPRECATED_MSG("The method get_bounce has been deprecated and will be removed in the future, use physics material instead.");
if (physics_material_override.is_null()) {
return 0;
@@ -905,10 +891,7 @@ void RigidBody2D::set_contact_monitor(bool p_enabled) {
if (!p_enabled) {
- if (contact_monitor->locked) {
- ERR_EXPLAIN("Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\",false) instead");
- }
- ERR_FAIL_COND(contact_monitor->locked);
+ ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead.");
for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
@@ -963,7 +946,7 @@ String RigidBody2D::get_configuration_warning() const {
if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) {
if (warning != String()) {
- warning += "\n";
+ warning += "\n\n";
}
warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.");
}
@@ -1074,10 +1057,10 @@ void RigidBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
ADD_GROUP("Angular", "angular_");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_GROUP("Applied Forces", "applied_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "applied_force"), "set_applied_force", "get_applied_force");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "applied_torque"), "set_applied_torque", "get_applied_torque");
@@ -1226,23 +1209,25 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
return colliding;
}
-//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45.
+//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45.
#define FLOOR_ANGLE_THRESHOLD 0.01
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
- Vector2 floor_motion = floor_velocity;
+ Vector2 body_velocity = p_linear_velocity;
+ Vector2 body_velocity_normal = body_velocity.normalized();
+
+ Vector2 current_floor_velocity = floor_velocity;
if (on_floor && on_floor_body.is_valid()) {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body);
if (bs) {
- floor_motion = bs->get_linear_velocity();
+ current_floor_velocity = bs->get_linear_velocity();
}
}
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
- Vector2 motion = (floor_motion + p_linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
- Vector2 lv = p_linear_velocity;
+ Vector2 motion = (current_floor_velocity + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time());
on_floor = false;
on_floor_body = RID();
@@ -1251,14 +1236,12 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
colliders.clear();
floor_velocity = Vector2();
- Vector2 lv_n = p_linear_velocity.normalized();
-
while (p_max_slides) {
Collision collision;
bool found_collision = false;
- for (int i = 0; i < 2; i++) {
+ for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
collided = move_and_collide(motion, p_infinite_inertia, collision);
@@ -1275,9 +1258,6 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
if (collided) {
found_collision = true;
- }
-
- if (collided) {
colliders.push_back(collision);
motion = collision.remainder;
@@ -1286,43 +1266,39 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
//all is a wall
on_wall = true;
} else {
- if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor
+ if (Math::acos(collision.normal.dot(p_floor_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor
on_floor = true;
on_floor_body = collision.collider_rid;
floor_velocity = collision.collider_vel;
if (p_stop_on_slope) {
- if ((lv_n + p_floor_direction).length() < 0.01 && collision.travel.length() < 1) {
+ if ((body_velocity_normal + p_floor_direction).length() < 0.01 && collision.travel.length() < 1) {
Transform2D gt = get_global_transform();
- gt.elements[2] -= collision.travel.project(p_floor_direction.tangent());
+ gt.elements[2] -= collision.travel.slide(p_floor_direction);
set_global_transform(gt);
return Vector2();
}
}
-
- } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling
+ } else if (Math::acos(collision.normal.dot(-p_floor_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling
on_ceiling = true;
} else {
on_wall = true;
}
}
- Vector2 n = collision.normal;
- motion = motion.slide(n);
- lv = lv.slide(n);
+ motion = motion.slide(collision.normal);
+ body_velocity = body_velocity.slide(collision.normal);
}
}
- if (!found_collision) {
- break;
- }
- p_max_slides--;
- if (motion == Vector2())
+ if (!found_collision || motion == Vector2())
break;
+
+ --p_max_slides;
}
- return lv;
+ return body_velocity;
}
Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) {
@@ -1466,6 +1442,14 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) {
void KinematicBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
last_valid_transform = get_global_transform();
+
+ // Reset move_and_slide() data.
+ on_floor = false;
+ on_floor_body = RID();
+ on_ceiling = false;
+ on_wall = false;
+ colliders.clear();
+ floor_velocity = Vector2();
}
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
@@ -1544,7 +1528,7 @@ Vector2 KinematicCollision2D::get_remainder() const {
return collision.remainder;
}
Object *KinematicCollision2D::get_local_shape() const {
- ERR_FAIL_COND_V(!owner, NULL);
+ if (!owner) return NULL;
uint32_t ownerid = owner->shape_find_owner(collision.local_shape);
return owner->shape_owner_get_owner(ownerid);
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 89dd1e5341..66e5ce250f 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -157,8 +157,8 @@ private:
bool operator<(const ShapePair &p_sp) const {
if (body_shape == p_sp.body_shape)
return local_shape < p_sp.local_shape;
- else
- return body_shape < p_sp.body_shape;
+
+ return body_shape < p_sp.body_shape;
}
ShapePair() {}
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index f6f1bad581..9fe67a4d7e 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -76,7 +76,7 @@ Rect2 Polygon2D::_edit_get_rect() const {
}
bool Polygon2D::_edit_use_rect() const {
- return true;
+ return polygon.size() > 0;
}
bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
@@ -308,7 +308,7 @@ void Polygon2D::_notification(int p_what) {
if (invert || polygons.size() == 0) {
Vector<int> indices = Geometry::triangulate_polygon(points);
if (indices.size()) {
- VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, RID(), antialiased);
}
} else {
//draw individual polygons
@@ -342,7 +342,7 @@ void Polygon2D::_notification(int p_what) {
}
if (total_indices.size()) {
- VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID(), -1, RID(), antialiased);
}
#if 0
@@ -842,7 +842,7 @@ void Polygon2D::_bind_methods() {
ADD_GROUP("Texture", "texture_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_offset"), "set_texture_offset", "get_texture_offset");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
ADD_GROUP("Skeleton", "");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton2D"), "set_skeleton", "get_skeleton");
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index bed6f8a816..e37407ceb3 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -33,15 +33,20 @@
#include "core/engine.h"
#include "scene/resources/texture.h"
+const float DEFAULT_GIZMO_EXTENTS = 10.0;
+
void Position2D::_draw_cross() {
- draw_line(Point2(-10, 0), Point2(+10, 0), Color(1, 0.5, 0.5));
- draw_line(Point2(0, -10), Point2(0, +10), Color(0.5, 1, 0.5));
+ float extents = get_gizmo_extents();
+ // Colors taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`)
+ draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(0.96, 0.20, 0.32));
+ draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.53, 0.84, 0.01));
}
Rect2 Position2D::_edit_get_rect() const {
- return Rect2(Point2(-10, -10), Size2(20, 20));
+ float extents = get_gizmo_extents();
+ return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2));
}
bool Position2D::_edit_use_rect() const {
@@ -66,5 +71,31 @@ void Position2D::_notification(int p_what) {
}
}
+void Position2D::set_gizmo_extents(float p_extents) {
+ if (p_extents == DEFAULT_GIZMO_EXTENTS) {
+ set_meta("_gizmo_extents_", Variant());
+ } else {
+ set_meta("_gizmo_extents_", p_extents);
+ }
+
+ update();
+}
+
+float Position2D::get_gizmo_extents() const {
+ if (has_meta("_gizmo_extents_")) {
+ return get_meta("_gizmo_extents_");
+ } else {
+ return DEFAULT_GIZMO_EXTENTS;
+ }
+}
+
+void Position2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_set_gizmo_extents", "extents"), &Position2D::set_gizmo_extents);
+ ClassDB::bind_method(D_METHOD("_get_gizmo_extents"), &Position2D::get_gizmo_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gizmo_extents", PROPERTY_HINT_RANGE, "0,1000,0.1,or_greater", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_gizmo_extents", "_get_gizmo_extents");
+}
+
Position2D::Position2D() {
}
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index c95315fea3..f5ec3ef01a 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -35,16 +35,21 @@
class Position2D : public Node2D {
- GDCLASS(Position2D, Node2D)
+ GDCLASS(Position2D, Node2D);
void _draw_cross();
protected:
void _notification(int p_what);
+ static void _bind_methods();
public:
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
+
+ void set_gizmo_extents(float p_extents);
+ float get_gizmo_extents() const;
+
Position2D();
};
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 57dfe5176d..4f6f410bdc 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -100,6 +100,7 @@ Vector2 RayCast2D::get_collision_normal() const {
void RayCast2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
+ update();
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
set_physics_process_internal(p_enabled);
if (!p_enabled)
@@ -167,19 +168,25 @@ void RayCast2D::_notification(int p_what) {
xf.rotate(cast_to.angle());
xf.translate(Vector2(cast_to.length(), 0));
- //Vector2 tip = Vector2(0,s->get_length());
- Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
- draw_line(Vector2(), cast_to, dcol, 3);
+ // Draw an arrow indicating where the RayCast is pointing to
+ Color draw_col = get_tree()->get_debug_collisions_color();
+ if (!enabled) {
+ float g = draw_col.get_v();
+ draw_col.r = g;
+ draw_col.g = g;
+ draw_col.b = g;
+ }
+ draw_line(Vector2(), cast_to, draw_col, 2, true);
Vector<Vector2> pts;
- float tsize = 4;
+ float tsize = 8;
pts.push_back(xf.xform(Vector2(tsize, 0)));
- pts.push_back(xf.xform(Vector2(0, 0.707 * tsize)));
- pts.push_back(xf.xform(Vector2(0, -0.707 * tsize)));
+ pts.push_back(xf.xform(Vector2(0, Math_SQRT12 * tsize)));
+ pts.push_back(xf.xform(Vector2(0, -Math_SQRT12 * tsize)));
Vector<Color> cols;
for (int i = 0; i < 3; i++)
- cols.push_back(dcol);
+ cols.push_back(draw_col);
- draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ draw_primitive(pts, cols, Vector<Vector2>());
} break;
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index 1c38a91877..fe8cc5a5fc 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -110,7 +110,7 @@ void RemoteTransform2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_ENTER_TREE: {
_update_cache();
@@ -180,6 +180,10 @@ bool RemoteTransform2D::get_update_scale() const {
return update_remote_scale;
}
+void RemoteTransform2D::force_update_cache() {
+ _update_cache();
+}
+
String RemoteTransform2D::get_configuration_warning() const {
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
@@ -193,6 +197,7 @@ void RemoteTransform2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_remote_node", "path"), &RemoteTransform2D::set_remote_node);
ClassDB::bind_method(D_METHOD("get_remote_node"), &RemoteTransform2D::get_remote_node);
+ ClassDB::bind_method(D_METHOD("force_update_cache"), &RemoteTransform2D::force_update_cache);
ClassDB::bind_method(D_METHOD("set_use_global_coordinates", "use_global_coordinates"), &RemoteTransform2D::set_use_global_coordinates);
ClassDB::bind_method(D_METHOD("get_use_global_coordinates"), &RemoteTransform2D::get_use_global_coordinates);
diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h
index 16a5417592..e85fe33fc6 100644
--- a/scene/2d/remote_transform_2d.h
+++ b/scene/2d/remote_transform_2d.h
@@ -69,6 +69,8 @@ public:
void set_update_scale(const bool p_update);
bool get_update_scale() const;
+ void force_update_cache();
+
virtual String get_configuration_warning() const;
RemoteTransform2D();
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index aa15255384..bf43fca864 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -137,7 +137,7 @@ String Bone2D::get_configuration_warning() const {
String warning = Node2D::get_configuration_warning();
if (!skeleton) {
if (warning != String()) {
- warning += "\n";
+ warning += "\n\n";
}
if (parent_bone) {
warning += TTR("This Bone2D chain should end at a Skeleton2D node.");
@@ -148,7 +148,7 @@ String Bone2D::get_configuration_warning() const {
if (rest == Transform2D(0, 0, 0, 0, 0, 0)) {
if (warning != String()) {
- warning += "\n";
+ warning += "\n\n";
}
warning += TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index cf9877e6f8..0f48b44387 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -36,9 +36,12 @@
class Skeleton2D;
class Bone2D : public Node2D {
- GDCLASS(Bone2D, Node2D)
+ GDCLASS(Bone2D, Node2D);
friend class Skeleton2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
Bone2D *parent_bone;
Skeleton2D *skeleton;
@@ -71,6 +74,9 @@ class Skeleton2D : public Node2D {
GDCLASS(Skeleton2D, Node2D);
friend class Bone2D;
+#ifdef TOOLS_ENABLED
+ friend class AnimatedValuesBackup;
+#endif
struct Bone {
bool operator<(const Bone &p_bone) const {
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index a8c7622828..8cdfceea52 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -63,10 +63,7 @@ Rect2 Sprite::_edit_get_rect() const {
}
bool Sprite::_edit_use_rect() const {
- if (texture.is_null())
- return false;
-
- return true;
+ return texture.is_valid();
}
Rect2 Sprite::get_anchorable_rect() const {
@@ -138,12 +135,12 @@ void Sprite::set_texture(const Ref<Texture> &p_texture) {
return;
if (texture.is_valid())
- texture->remove_change_receptor(this);
+ texture->disconnect(CoreStringNames::get_singleton()->changed, this, "_texture_changed");
texture = p_texture;
if (texture.is_valid())
- texture->add_change_receptor(this);
+ texture->connect(CoreStringNames::get_singleton()->changed, this, "_texture_changed");
update();
emit_signal("texture_changed");
@@ -262,6 +259,7 @@ void Sprite::set_frame(int p_frame) {
frame = p_frame;
_change_notify("frame");
+ _change_notify("frame_coords");
emit_signal(SceneStringNames::get_singleton()->frame_changed);
}
@@ -270,9 +268,20 @@ int Sprite::get_frame() const {
return frame;
}
+void Sprite::set_frame_coords(const Vector2 &p_coord) {
+ ERR_FAIL_INDEX(int(p_coord.x), hframes);
+ ERR_FAIL_INDEX(int(p_coord.y), vframes);
+
+ set_frame(int(p_coord.y) * hframes + int(p_coord.x));
+}
+
+Vector2 Sprite::get_frame_coords() const {
+ return Vector2(frame % hframes, frame / hframes);
+}
+
void Sprite::set_vframes(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1.");
vframes = p_amount;
update();
item_rect_changed();
@@ -285,7 +294,7 @@ int Sprite::get_vframes() const {
void Sprite::set_hframes(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1.");
hframes = p_amount;
update();
item_rect_changed();
@@ -374,18 +383,21 @@ Rect2 Sprite::get_rect() const {
void Sprite::_validate_property(PropertyInfo &property) const {
if (property.name == "frame") {
-
- property.hint = PROPERTY_HINT_SPRITE_FRAME;
-
+ property.hint = PROPERTY_HINT_RANGE;
property.hint_string = "0," + itos(vframes * hframes - 1) + ",1";
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
+ }
+
+ if (property.name == "frame_coords") {
+ property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
}
-void Sprite::_changed_callback(Object *p_changed, const char *p_prop) {
+void Sprite::_texture_changed() {
// Changes to the texture need to trigger an update to make
// the editor redraw the sprite with the updated texture.
- if (texture.is_valid() && texture.ptr() == p_changed) {
+ if (texture.is_valid()) {
update();
}
}
@@ -424,6 +436,9 @@ void Sprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_frame", "frame"), &Sprite::set_frame);
ClassDB::bind_method(D_METHOD("get_frame"), &Sprite::get_frame);
+ ClassDB::bind_method(D_METHOD("set_frame_coords", "coords"), &Sprite::set_frame_coords);
+ ClassDB::bind_method(D_METHOD("get_frame_coords"), &Sprite::get_frame_coords);
+
ClassDB::bind_method(D_METHOD("set_vframes", "vframes"), &Sprite::set_vframes);
ClassDB::bind_method(D_METHOD("get_vframes"), &Sprite::get_vframes);
@@ -432,6 +447,8 @@ void Sprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rect"), &Sprite::get_rect);
+ ClassDB::bind_method(D_METHOD("_texture_changed"), &Sprite::_texture_changed);
+
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_SIGNAL(MethodInfo("texture_changed"));
@@ -445,7 +462,8 @@ void Sprite::_bind_methods() {
ADD_GROUP("Animation", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords");
ADD_GROUP("Region", "region_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region");
@@ -468,6 +486,4 @@ Sprite::Sprite() {
}
Sprite::~Sprite() {
- if (texture.is_valid())
- texture->remove_change_receptor(this);
}
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index e38db3a299..d3c0112e62 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -57,6 +57,8 @@ class Sprite : public Node2D {
void _get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const;
+ void _texture_changed();
+
protected:
void _notification(int p_what);
@@ -64,8 +66,6 @@ protected:
virtual void _validate_property(PropertyInfo &property) const;
- virtual void _changed_callback(Object *p_changed, const char *p_prop);
-
public:
virtual Dictionary _edit_get_state() const;
virtual void _edit_set_state(const Dictionary &p_state);
@@ -110,6 +110,9 @@ public:
void set_frame(int p_frame);
int get_frame() const;
+ void set_frame_coords(const Vector2 &p_coord);
+ Vector2 get_frame_coords() const;
+
void set_vframes(int p_amount);
int get_vframes() const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 3562011bc2..d75d8cfc55 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -30,9 +30,11 @@
#include "tile_map.h"
+#include "collision_object_2d.h"
#include "core/io/marshalls.h"
#include "core/method_bind_ext.gen.inc"
#include "core/os/os.h"
+#include "scene/2d/area_2d.h"
#include "servers/physics_2d_server.h"
int TileMap::_get_quadrant_size() const {
@@ -60,14 +62,21 @@ void TileMap::_notification(int p_what) {
c = Object::cast_to<Node2D>(c->get_parent());
}
+ if (use_parent) {
+ _clear_quadrants();
+ collision_parent = Object::cast_to<CollisionObject2D>(get_parent());
+ }
+
pending_update = true;
_recreate_quadrants();
update_dirty_quadrants();
RID space = get_world_2d()->get_space();
_update_quadrant_transform();
_update_quadrant_space(space);
+ update_configuration_warning();
} break;
+
case NOTIFICATION_EXIT_TREE: {
_update_quadrant_space(RID());
@@ -82,30 +91,46 @@ void TileMap::_notification(int p_what) {
q.navpoly_ids.clear();
}
+ if (collision_parent) {
+ collision_parent->remove_shape_owner(q.shape_owner_id);
+ q.shape_owner_id = -1;
+ }
+
for (Map<PosKey, Quadrant::Occluder>::Element *F = q.occluder_instances.front(); F; F = F->next()) {
VS::get_singleton()->free(F->get().id);
}
q.occluder_instances.clear();
}
+ collision_parent = NULL;
navigation = NULL;
} break;
+
case NOTIFICATION_TRANSFORM_CHANGED: {
//move stuff
_update_quadrant_transform();
} break;
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+
+ if (use_parent) {
+ _recreate_quadrants();
+ }
+
+ } break;
}
}
void TileMap::_update_quadrant_space(const RID &p_space) {
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!use_parent) {
+ for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Physics2DServer::get_singleton()->body_set_space(q.body, p_space);
+ Quadrant &q = E->get();
+ Physics2DServer::get_singleton()->body_set_space(q.body, p_space);
+ }
}
}
@@ -116,6 +141,10 @@ void TileMap::_update_quadrant_transform() {
Transform2D global_transform = get_global_transform();
+ Transform2D local_transform;
+ if (collision_parent)
+ local_transform = get_transform();
+
Transform2D nav_rel;
if (navigation)
nav_rel = get_relative_transform_to_parent(navigation);
@@ -125,8 +154,11 @@ void TileMap::_update_quadrant_transform() {
Quadrant &q = E->get();
Transform2D xform;
xform.set_origin(q.pos);
- xform = global_transform * xform;
- Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform);
+
+ if (!use_parent) {
+ xform = global_transform * xform;
+ Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform);
+ }
if (navigation) {
for (Map<PosKey, Quadrant::NavPoly>::Element *F = q.navpoly_ids.front(); F; F = F->next()) {
@@ -184,7 +216,7 @@ Size2 TileMap::get_cell_size() const {
void TileMap::set_quadrant_size(int p_size) {
- ERR_FAIL_COND(p_size < 1);
+ ERR_FAIL_COND_MSG(p_size < 1, "Quadrant size cannot be smaller than 1.");
_clear_quadrants();
quadrant_size = p_size;
@@ -202,18 +234,23 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
Size2 s = p_sc;
Vector2 offset = p_offset;
- if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
- offset.y += cell_size.y;
- else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset += cell_size / 2;
- }
+ if (compatibility_mode && !centered_textures) {
- if (s.y > s.x) {
- if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose))
- offset.y += s.y - s.x;
- } else if (s.y < s.x) {
- if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose))
- offset.x += s.x - s.y;
+ if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
+ offset.y += cell_size.y;
+ } else if (tile_origin == TILE_ORIGIN_CENTER) {
+ offset += cell_size / 2;
+ }
+
+ if (s.y > s.x) {
+ if ((p_cell.flip_h && (p_cell.flip_v || p_cell.transpose)) || (p_cell.flip_v && !p_cell.transpose)) {
+ offset.y += s.y - s.x;
+ }
+ } else if (s.y < s.x) {
+ if ((p_cell.flip_v && (p_cell.flip_h || p_cell.transpose)) || (p_cell.flip_h && !p_cell.transpose)) {
+ offset.x += s.x - s.y;
+ }
+ }
}
if (p_cell.transpose) {
@@ -222,27 +259,69 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const
SWAP(offset.x, offset.y);
SWAP(s.x, s.y);
}
+
if (p_cell.flip_h) {
xform.elements[0].x = -xform.elements[0].x;
xform.elements[1].x = -xform.elements[1].x;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT)
+ if (compatibility_mode && !centered_textures) {
+ if (tile_origin == TILE_ORIGIN_TOP_LEFT || tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
+ offset.x = s.x - offset.x;
+ } else if (tile_origin == TILE_ORIGIN_CENTER) {
+ offset.x = s.x - offset.x / 2;
+ }
+ } else {
offset.x = s.x - offset.x;
- else if (tile_origin == TILE_ORIGIN_CENTER)
- offset.x = s.x - offset.x / 2;
+ }
}
+
if (p_cell.flip_v) {
xform.elements[0].y = -xform.elements[0].y;
xform.elements[1].y = -xform.elements[1].y;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT)
+ if (compatibility_mode && !centered_textures) {
+ if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
+ offset.y = s.y - offset.y;
+ } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
+ offset.y += s.y;
+ } else if (tile_origin == TILE_ORIGIN_CENTER) {
+ offset.y += s.y;
+ }
+ } else {
offset.y = s.y - offset.y;
- else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- offset.y += s.y;
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
- offset.y += s.y;
}
}
- xform.elements[2].x += offset.x;
- xform.elements[2].y += offset.y;
+
+ if (centered_textures) {
+ offset += cell_size / 2 - s / 2;
+ }
+ xform.elements[2] += offset;
+}
+
+void TileMap::_add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata) {
+ Physics2DServer *ps = Physics2DServer::get_singleton();
+
+ if (!use_parent) {
+ ps->body_add_shape(p_q.body, p_shape->get_rid(), p_xform);
+ ps->body_set_shape_metadata(p_q.body, shape_idx, p_metadata);
+ ps->body_set_shape_as_one_way_collision(p_q.body, shape_idx, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin);
+
+ } else if (collision_parent) {
+ Transform2D xform = p_xform;
+ xform.set_origin(xform.get_origin() + p_q.pos);
+
+ collision_parent->shape_owner_add_shape(p_q.shape_owner_id, p_shape);
+
+ int real_index = collision_parent->shape_owner_get_shape_index(p_q.shape_owner_id, shape_idx);
+ RID rid = collision_parent->get_rid();
+
+ if (Object::cast_to<Area2D>(collision_parent) != NULL) {
+ ps->area_set_shape_transform(rid, real_index, get_transform() * xform);
+ } else {
+ ps->body_set_shape_transform(rid, real_index, get_transform() * xform);
+ ps->body_set_shape_metadata(rid, real_index, p_metadata);
+ ps->body_set_shape_as_one_way_collision(rid, real_index, p_shape_data.one_way_collision, p_shape_data.one_way_collision_margin);
+ }
+ }
+ shape_idx++;
}
void TileMap::update_dirty_quadrants() {
@@ -288,7 +367,11 @@ void TileMap::update_dirty_quadrants() {
q.canvas_items.clear();
- ps->body_clear_shapes(q.body);
+ if (!use_parent) {
+ ps->body_clear_shapes(q.body);
+ } else if (collision_parent) {
+ collision_parent->shape_owner_clear_shapes(q.shape_owner_id);
+ }
int shape_idx = 0;
if (navigation) {
@@ -390,63 +473,72 @@ void TileMap::update_dirty_quadrants() {
rect.size.x += fp_adjust;
rect.size.y += fp_adjust;
- if (rect.size.y > rect.size.x) {
- if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose))
- tile_ofs.y += rect.size.y - rect.size.x;
- } else if (rect.size.y < rect.size.x) {
- if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose))
- tile_ofs.x += rect.size.x - rect.size.y;
+ if (compatibility_mode && !centered_textures) {
+ if (rect.size.y > rect.size.x) {
+ if ((c.flip_h && (c.flip_v || c.transpose)) || (c.flip_v && !c.transpose))
+ tile_ofs.y += rect.size.y - rect.size.x;
+ } else if (rect.size.y < rect.size.x) {
+ if ((c.flip_v && (c.flip_h || c.transpose)) || (c.flip_h && !c.transpose))
+ tile_ofs.x += rect.size.x - rect.size.y;
+ }
}
- /* rect.size.x+=fp_adjust;
- rect.size.y+=fp_adjust;*/
-
- if (c.transpose)
+ if (c.transpose) {
SWAP(tile_ofs.x, tile_ofs.y);
+ if (centered_textures) {
+ rect.position.x += cell_size.x / 2 - rect.size.y / 2;
+ rect.position.y += cell_size.y / 2 - rect.size.x / 2;
+ }
+ } else if (centered_textures) {
+ rect.position += cell_size / 2 - rect.size / 2;
+ }
if (c.flip_h) {
rect.size.x = -rect.size.x;
tile_ofs.x = -tile_ofs.x;
}
+
if (c.flip_v) {
rect.size.y = -rect.size.y;
tile_ofs.y = -tile_ofs.y;
}
- Vector2 center_ofs;
+ if (compatibility_mode && !centered_textures) {
+ if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
+ rect.position += tile_ofs;
- if (tile_origin == TILE_ORIGIN_TOP_LEFT) {
- rect.position += tile_ofs;
+ } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
- } else if (tile_origin == TILE_ORIGIN_BOTTOM_LEFT) {
+ rect.position += tile_ofs;
- rect.position += tile_ofs;
+ if (c.transpose) {
+ if (c.flip_h)
+ rect.position.x -= cell_size.x;
+ else
+ rect.position.x += cell_size.x;
+ } else {
+ if (c.flip_v)
+ rect.position.y -= cell_size.y;
+ else
+ rect.position.y += cell_size.y;
+ }
+
+ } else if (tile_origin == TILE_ORIGIN_CENTER) {
+
+ rect.position += tile_ofs;
- if (c.transpose) {
if (c.flip_h)
- rect.position.x -= cell_size.x;
+ rect.position.x -= cell_size.x / 2;
else
- rect.position.x += cell_size.x;
- } else {
+ rect.position.x += cell_size.x / 2;
+
if (c.flip_v)
- rect.position.y -= cell_size.y;
+ rect.position.y -= cell_size.y / 2;
else
- rect.position.y += cell_size.y;
+ rect.position.y += cell_size.y / 2;
}
-
- } else if (tile_origin == TILE_ORIGIN_CENTER) {
-
+ } else {
rect.position += tile_ofs;
-
- if (c.flip_h)
- rect.position.x -= cell_size.x / 2;
- else
- rect.position.x += cell_size.x / 2;
-
- if (c.flip_v)
- rect.position.y -= cell_size.y / 2;
- else
- rect.position.y += cell_size.y / 2;
}
Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id);
@@ -471,7 +563,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 shape_ofs = shapes[j].shape_transform.get_origin();
- _fix_cell_transform(xform, c, shape_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, shape_ofs, s);
xform *= shapes[j].shape_transform.untranslated();
@@ -485,21 +577,15 @@ void TileMap::update_dirty_quadrants() {
for (int k = 0; k < _shapes.size(); k++) {
Ref<ConvexPolygonShape2D> convex = _shapes[k];
if (convex.is_valid()) {
- ps->body_add_shape(q.body, convex->get_rid(), xform);
- ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y));
- ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
- shape_idx++;
+ _add_shape(shape_idx, q, convex, shapes[j], xform, Vector2(E->key().x, E->key().y));
#ifdef DEBUG_ENABLED
} else {
- print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape.");
+ print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape.");
#endif
}
}
} else {
- ps->body_add_shape(q.body, shape->get_rid(), xform);
- ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y));
- ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[j].one_way_collision, shapes[j].one_way_collision_margin);
- shape_idx++;
+ _add_shape(shape_idx, q, shape, shapes[j], xform, Vector2(E->key().x, E->key().y));
}
}
}
@@ -523,7 +609,7 @@ void TileMap::update_dirty_quadrants() {
if (navpoly.is_valid()) {
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, npoly_ofs, s);
int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
@@ -573,7 +659,7 @@ void TileMap::update_dirty_quadrants() {
}
Transform2D navxform;
navxform.set_origin(offset.floor());
- _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s);
+ _fix_cell_transform(navxform, c, npoly_ofs, s);
vs->canvas_item_set_transform(debug_navigation_item, navxform);
vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
@@ -593,7 +679,7 @@ void TileMap::update_dirty_quadrants() {
Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
- _fix_cell_transform(xform, c, occluder_ofs + center_ofs, s);
+ _fix_cell_transform(xform, c, occluder_ofs, s);
RID orid = VS::get_singleton()->canvas_light_occluder_create();
VS::get_singleton()->canvas_light_occluder_set_transform(orid, get_global_transform() * xform);
@@ -674,22 +760,29 @@ Map<TileMap::PosKey, TileMap::Quadrant>::Element *TileMap::_create_quadrant(cons
xform.set_origin(q.pos);
//q.canvas_item = VisualServer::get_singleton()->canvas_item_create();
- q.body = Physics2DServer::get_singleton()->body_create();
- Physics2DServer::get_singleton()->body_set_mode(q.body, use_kinematic ? Physics2DServer::BODY_MODE_KINEMATIC : Physics2DServer::BODY_MODE_STATIC);
-
- Physics2DServer::get_singleton()->body_attach_object_instance_id(q.body, get_instance_id());
- Physics2DServer::get_singleton()->body_set_collision_layer(q.body, collision_layer);
- Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask);
- Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, friction);
- Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, bounce);
-
- if (is_inside_tree()) {
- xform = get_global_transform() * xform;
- RID space = get_world_2d()->get_space();
- Physics2DServer::get_singleton()->body_set_space(q.body, space);
- }
+ if (!use_parent) {
+ q.body = Physics2DServer::get_singleton()->body_create();
+ Physics2DServer::get_singleton()->body_set_mode(q.body, use_kinematic ? Physics2DServer::BODY_MODE_KINEMATIC : Physics2DServer::BODY_MODE_STATIC);
+
+ Physics2DServer::get_singleton()->body_attach_object_instance_id(q.body, get_instance_id());
+ Physics2DServer::get_singleton()->body_set_collision_layer(q.body, collision_layer);
+ Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask);
+ Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, friction);
+ Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, bounce);
+
+ if (is_inside_tree()) {
+ xform = get_global_transform() * xform;
+ RID space = get_world_2d()->get_space();
+ Physics2DServer::get_singleton()->body_set_space(q.body, space);
+ }
- Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform);
+ Physics2DServer::get_singleton()->body_set_state(q.body, Physics2DServer::BODY_STATE_TRANSFORM, xform);
+ } else if (collision_parent) {
+ xform = get_transform() * xform;
+ q.shape_owner_id = collision_parent->create_shape_owner(this);
+ } else {
+ q.shape_owner_id = -1;
+ }
rect_cache_dirty = true;
quadrant_order_dirty = true;
@@ -699,7 +792,12 @@ Map<TileMap::PosKey, TileMap::Quadrant>::Element *TileMap::_create_quadrant(cons
void TileMap::_erase_quadrant(Map<PosKey, Quadrant>::Element *Q) {
Quadrant &q = Q->get();
- Physics2DServer::get_singleton()->free(q.body);
+ if (!use_parent) {
+ Physics2DServer::get_singleton()->free(q.body);
+ } else if (collision_parent) {
+ collision_parent->remove_shape_owner(q.shape_owner_id);
+ }
+
for (List<RID>::Element *E = q.canvas_items.front(); E; E = E->next()) {
VisualServer::get_singleton()->free(E->get());
@@ -749,7 +847,10 @@ void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_
void TileMap::_set_celld(const Vector2 &p_pos, const Dictionary &p_data) {
- set_cell(p_pos.x, p_pos.y, p_data["id"], p_data["flip_h"], p_data["flip_y"], p_data["transpose"], p_data["auto_coord"]);
+ Variant v_pos_x = p_pos.x, v_pos_y = p_pos.y, v_tile = p_data["id"], v_flip_h = p_data["flip_h"], v_flip_v = p_data["flip_y"], v_transpose = p_data["transpose"], v_autotile_coord = p_data["auto_coord"];
+ const Variant *args[7] = { &v_pos_x, &v_pos_y, &v_tile, &v_flip_h, &v_flip_v, &v_transpose, &v_autotile_coord };
+ Variant::CallError ce;
+ call("set_cell", args, 7, ce);
}
void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose, Vector2 p_autotile_coord) {
@@ -760,7 +861,7 @@ void TileMap::set_cell(int p_x, int p_y, int p_tile, bool p_flip_x, bool p_flip_
if (!E && p_tile == INVALID_CELL)
return; //nothing to do
- PosKey qk(p_x / _get_quadrant_size(), p_y / _get_quadrant_size());
+ PosKey qk = pk.to_quadrant(_get_quadrant_size());
if (p_tile == INVALID_CELL) {
//erase existing
tile_map.erase(pk);
@@ -854,6 +955,7 @@ void TileMap::update_bitmask_region(const Vector2 &p_start, const Vector2 &p_end
void TileMap::update_cell_bitmask(int p_x, int p_y) {
+ ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot update cell bitmask if Tileset is not open.");
PosKey p(p_x, p_y);
Map<PosKey, Cell>::Element *E = tile_map.find(p);
if (E != NULL) {
@@ -919,13 +1021,22 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
E->get().autotile_coord_x = (int)coord.x;
E->get().autotile_coord_y = (int)coord.y;
- PosKey qk(p_x / _get_quadrant_size(), p_y / _get_quadrant_size());
+ PosKey qk = p.to_quadrant(_get_quadrant_size());
Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
_make_quadrant_dirty(Q);
} else if (tile_set->tile_get_tile_mode(id) == TileSet::SINGLE_TILE) {
+
E->get().autotile_coord_x = 0;
E->get().autotile_coord_y = 0;
+ } else if (tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) {
+
+ if (tile_set->autotile_get_bitmask(id, Vector2(p_x, p_y)) == TileSet::BIND_CENTER) {
+ Vector2 coord = tile_set->atlastile_get_subtile_by_priority(id, this, Vector2(p_x, p_y));
+
+ E->get().autotile_coord_x = (int)coord.x;
+ E->get().autotile_coord_y = (int)coord.y;
+ }
}
}
}
@@ -940,6 +1051,7 @@ void TileMap::update_dirty_bitmask() {
void TileMap::fix_invalid_tiles() {
+ ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
if (!tile_set->has_tile(get_cell(E->key().x, E->key().y))) {
@@ -1007,7 +1119,7 @@ void TileMap::set_cell_autotile_coord(int p_x, int p_y, const Vector2 &p_coord)
c.autotile_coord_y = p_coord.y;
tile_map[pk] = c;
- PosKey qk(p_x / _get_quadrant_size(), p_y / _get_quadrant_size());
+ PosKey qk = pk.to_quadrant(_get_quadrant_size());
Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
if (!Q)
@@ -1034,7 +1146,7 @@ void TileMap::_recreate_quadrants() {
for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
- PosKey qk(E->key().x / _get_quadrant_size(), E->key().y / _get_quadrant_size());
+ PosKey qk = PosKey(E->key().x, E->key().y).to_quadrant(_get_quadrant_size());
Map<PosKey, Quadrant>::Element *Q = quadrant_map.find(qk);
if (!Q) {
@@ -1093,6 +1205,8 @@ void TileMap::clear() {
void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
+ ERR_FAIL_COND(format > FORMAT_2);
+
int c = p_data.size();
PoolVector<int>::Read r = p_data.read();
@@ -1119,8 +1233,8 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
}
#endif
- int16_t x = decode_uint16(&local[0]);
- int16_t y = decode_uint16(&local[2]);
+ uint16_t x = decode_uint16(&local[0]);
+ uint16_t y = decode_uint16(&local[2]);
uint32_t v = decode_uint32(&local[4]);
bool flip_h = v & (1 << 29);
bool flip_v = v & (1 << 30);
@@ -1132,14 +1246,9 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
coord_x = decode_uint16(&local[8]);
coord_y = decode_uint16(&local[10]);
}
- /*
- if (x<-20 || y <-20 || x>4000 || y>4000)
- continue;
- */
+
set_cell(x, y, v, flip_h, flip_v, transpose, Vector2(coord_x, coord_y));
}
-
- format = FORMAT_2;
}
PoolVector<int> TileMap::_get_tile_data() const {
@@ -1148,7 +1257,7 @@ PoolVector<int> TileMap::_get_tile_data() const {
data.resize(tile_map.size() * 3);
PoolVector<int>::Write w = data.write();
- format = FORMAT_2;
+ // Save in highest format
int idx = 0;
for (const Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
@@ -1168,33 +1277,41 @@ PoolVector<int> TileMap::_get_tile_data() const {
idx += 3;
}
- w = PoolVector<int>::Write();
+ w.release();
return data;
}
Rect2 TileMap::_edit_get_rect() const {
- const_cast<TileMap *>(this)->update_dirty_quadrants();
+ if (pending_update) {
+ const_cast<TileMap *>(this)->update_dirty_quadrants();
+ } else {
+ const_cast<TileMap *>(this)->_recompute_rect_cache();
+ }
return rect_cache;
}
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!use_parent) {
+ for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Physics2DServer::get_singleton()->body_set_collision_layer(q.body, collision_layer);
+ Quadrant &q = E->get();
+ Physics2DServer::get_singleton()->body_set_collision_layer(q.body, collision_layer);
+ }
}
}
void TileMap::set_collision_mask(uint32_t p_mask) {
collision_mask = p_mask;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!use_parent) {
+ for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask);
+ Quadrant &q = E->get();
+ Physics2DServer::get_singleton()->body_set_collision_mask(q.body, collision_mask);
+ }
}
}
@@ -1230,13 +1347,40 @@ void TileMap::set_collision_use_kinematic(bool p_use_kinematic) {
_recreate_quadrants();
}
+bool TileMap::get_collision_use_parent() const {
+
+ return use_parent;
+}
+
+void TileMap::set_collision_use_parent(bool p_use_parent) {
+
+ if (use_parent == p_use_parent) return;
+
+ _clear_quadrants();
+
+ use_parent = p_use_parent;
+ set_notify_local_transform(use_parent);
+
+ if (use_parent && is_inside_tree()) {
+ collision_parent = Object::cast_to<CollisionObject2D>(get_parent());
+ } else {
+ collision_parent = NULL;
+ }
+
+ _recreate_quadrants();
+ _change_notify();
+ update_configuration_warning();
+}
+
void TileMap::set_collision_friction(float p_friction) {
friction = p_friction;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!use_parent) {
+ for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, p_friction);
+ Quadrant &q = E->get();
+ Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_FRICTION, p_friction);
+ }
}
}
@@ -1248,10 +1392,12 @@ float TileMap::get_collision_friction() const {
void TileMap::set_collision_bounce(float p_bounce) {
bounce = p_bounce;
- for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
+ if (!use_parent) {
+ for (Map<PosKey, Quadrant>::Element *E = quadrant_map.front(); E; E = E->next()) {
- Quadrant &q = E->get();
- Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, p_bounce);
+ Quadrant &q = E->get();
+ Physics2DServer::get_singleton()->body_set_param(q.body, Physics2DServer::BODY_PARAM_BOUNCE, p_bounce);
+ }
}
}
float TileMap::get_collision_bounce() const {
@@ -1405,7 +1551,9 @@ Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const {
ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5);
}
} break;
- default: {}
+ case HALF_OFFSET_DISABLED: {
+ // Nothing to do.
+ }
}
}
return ret;
@@ -1415,7 +1563,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "format") {
if (p_value.get_type() == Variant::INT) {
- format = (DataFormat)(p_value.operator int64_t());
+ format = (DataFormat)(p_value.operator int64_t()); // Set format used for loading
return true;
}
} else if (p_name == "tile_data") {
@@ -1431,7 +1579,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "format") {
- r_ret = format;
+ r_ret = FORMAT_2; // When saving, always save highest format
return true;
} else if (p_name == "tile_data") {
r_ret = _get_tile_data();
@@ -1449,6 +1597,12 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(p);
}
+void TileMap::_validate_property(PropertyInfo &property) const {
+ if (use_parent && property.name != "collision_use_parent" && property.name.begins_with("collision_")) {
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
Vector2 TileMap::map_to_world(const Vector2 &p_pos, bool p_ignore_ofs) const {
return _map_to_world(p_pos.x, p_pos.y, p_ignore_ofs);
@@ -1461,26 +1615,28 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const {
switch (half_offset) {
case HALF_OFFSET_X: {
- if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) {
+ if (int(floor(ret.y)) & 1) {
ret.x -= 0.5;
}
} break;
case HALF_OFFSET_NEGATIVE_X: {
- if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) {
+ if (int(floor(ret.y)) & 1) {
ret.x += 0.5;
}
} break;
case HALF_OFFSET_Y: {
- if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
+ if (int(floor(ret.x)) & 1) {
ret.y -= 0.5;
}
} break;
case HALF_OFFSET_NEGATIVE_Y: {
- if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
+ if (int(floor(ret.x)) & 1) {
ret.y += 0.5;
}
} break;
- default: {}
+ case HALF_OFFSET_DISABLED: {
+ // Nothing to do.
+ }
}
// Account for precision errors on the border (GH-23250).
@@ -1505,6 +1661,32 @@ bool TileMap::is_y_sort_mode_enabled() const {
return y_sort_mode;
}
+void TileMap::set_compatibility_mode(bool p_enable) {
+
+ _clear_quadrants();
+ compatibility_mode = p_enable;
+ _recreate_quadrants();
+ emit_signal("settings_changed");
+}
+
+bool TileMap::is_compatibility_mode_enabled() const {
+
+ return compatibility_mode;
+}
+
+void TileMap::set_centered_textures(bool p_enable) {
+
+ _clear_quadrants();
+ centered_textures = p_enable;
+ _recreate_quadrants();
+ emit_signal("settings_changed");
+}
+
+bool TileMap::is_centered_textures_enabled() const {
+
+ return centered_textures;
+}
+
Array TileMap::get_used_cells() const {
Array a;
@@ -1596,6 +1778,20 @@ bool TileMap::get_clip_uv() const {
return clip_uv;
}
+String TileMap::get_configuration_warning() const {
+
+ String warning = Node2D::get_configuration_warning();
+
+ if (use_parent && !collision_parent) {
+ if (!warning.empty()) {
+ warning += "\n\n";
+ }
+ return TTR("TileMap with Use Parent on needs a parent CollisionObject2D to give shapes to. Please use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+ }
+
+ return warning;
+}
+
void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tileset", "tileset"), &TileMap::set_tileset);
@@ -1628,9 +1824,18 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_y_sort_mode", "enable"), &TileMap::set_y_sort_mode);
ClassDB::bind_method(D_METHOD("is_y_sort_mode_enabled"), &TileMap::is_y_sort_mode_enabled);
+ ClassDB::bind_method(D_METHOD("set_compatibility_mode", "enable"), &TileMap::set_compatibility_mode);
+ ClassDB::bind_method(D_METHOD("is_compatibility_mode_enabled"), &TileMap::is_compatibility_mode_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_centered_textures", "enable"), &TileMap::set_centered_textures);
+ ClassDB::bind_method(D_METHOD("is_centered_textures_enabled"), &TileMap::is_centered_textures_enabled);
+
ClassDB::bind_method(D_METHOD("set_collision_use_kinematic", "use_kinematic"), &TileMap::set_collision_use_kinematic);
ClassDB::bind_method(D_METHOD("get_collision_use_kinematic"), &TileMap::get_collision_use_kinematic);
+ ClassDB::bind_method(D_METHOD("set_collision_use_parent", "use_parent"), &TileMap::set_collision_use_parent);
+ ClassDB::bind_method(D_METHOD("get_collision_use_parent"), &TileMap::get_collision_use_parent);
+
ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &TileMap::set_collision_layer);
ClassDB::bind_method(D_METHOD("get_collision_layer"), &TileMap::get_collision_layer);
@@ -1693,9 +1898,12 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_half_offset", PROPERTY_HINT_ENUM, "Offset X,Offset Y,Disabled,Offset Negative X,Offset Negative Y"), "set_half_offset", "get_half_offset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_tile_origin", PROPERTY_HINT_ENUM, "Top Left,Center,Bottom Left"), "set_tile_origin", "get_tile_origin");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_y_sort"), "set_y_sort_mode", "is_y_sort_mode_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "compatibility_mode"), "set_compatibility_mode", "is_compatibility_mode_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered_textures"), "set_centered_textures", "is_centered_textures_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_clip_uv"), "set_clip_uv", "get_clip_uv");
ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_parent", PROPERTY_HINT_NONE, ""), "set_collision_use_parent", "get_collision_use_parent");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_kinematic", PROPERTY_HINT_NONE, ""), "set_collision_use_kinematic", "get_collision_use_kinematic");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce");
@@ -1705,6 +1913,8 @@ void TileMap::_bind_methods() {
ADD_GROUP("Occluder", "occluder_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "occluder_light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
+ ADD_PROPERTY_DEFAULT("format", FORMAT_1);
+
ADD_SIGNAL(MethodInfo("settings_changed"));
BIND_CONSTANT(INVALID_CELL);
@@ -1738,22 +1948,28 @@ TileMap::TileMap() {
quadrant_order_dirty = false;
quadrant_size = 16;
cell_size = Size2(64, 64);
+ custom_transform = Transform2D(64, 0, 0, 64, 0, 0);
collision_layer = 1;
collision_mask = 1;
friction = 1;
bounce = 0;
mode = MODE_SQUARE;
half_offset = HALF_OFFSET_DISABLED;
+ use_parent = false;
+ collision_parent = NULL;
use_kinematic = false;
navigation = NULL;
y_sort_mode = false;
+ compatibility_mode = false;
+ centered_textures = false;
occluder_light_mask = 1;
clip_uv = false;
- format = FORMAT_1; //Always initialize with the lowest format
+ format = FORMAT_1; // Assume lowest possible format if none is present
fp_adjust = 0.00001;
tile_origin = TILE_ORIGIN_TOP_LEFT;
set_notify_transform(true);
+ set_notify_local_transform(false);
}
TileMap::~TileMap() {
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 6a1467aa48..e30b7eff83 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -37,6 +37,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/tile_set.h"
+class CollisionObject2D;
+
class TileMap : public Node2D {
GDCLASS(TileMap, Node2D);
@@ -74,6 +76,8 @@ private:
Mode mode;
Transform2D custom_transform;
HalfOffset half_offset;
+ bool use_parent;
+ CollisionObject2D *collision_parent;
bool use_kinematic;
Navigation2D *navigation;
@@ -90,6 +94,13 @@ private:
bool operator==(const PosKey &p_k) const { return (y == p_k.y && x == p_k.x); }
+ PosKey to_quadrant(const int &p_quadrant_size) const {
+ // rounding down, instead of simply rounding towards zero (truncating)
+ return PosKey(
+ x > 0 ? x / p_quadrant_size : (x - (p_quadrant_size - 1)) / p_quadrant_size,
+ y > 0 ? y / p_quadrant_size : (y - (p_quadrant_size - 1)) / p_quadrant_size);
+ }
+
PosKey(int16_t p_x, int16_t p_y) {
x = p_x;
y = p_y;
@@ -123,6 +134,7 @@ private:
Vector2 pos;
List<RID> canvas_items;
RID body;
+ uint32_t shape_owner_id;
SelfList<Quadrant> dirty_list;
@@ -145,6 +157,7 @@ private:
pos = q.pos;
canvas_items = q.canvas_items;
body = q.body;
+ shape_owner_id = q.shape_owner_id;
cells = q.cells;
navpoly_ids = q.navpoly_ids;
occluder_instances = q.occluder_instances;
@@ -154,6 +167,7 @@ private:
pos = q.pos;
canvas_items = q.canvas_items;
body = q.body;
+ shape_owner_id = q.shape_owner_id;
cells = q.cells;
occluder_instances = q.occluder_instances;
navpoly_ids = q.navpoly_ids;
@@ -174,6 +188,8 @@ private:
bool used_size_cache_dirty;
bool quadrant_order_dirty;
bool y_sort_mode;
+ bool compatibility_mode;
+ bool centered_textures;
bool clip_uv;
float fp_adjust;
float friction;
@@ -188,6 +204,8 @@ private:
void _fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
+ void _add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata);
+
Map<PosKey, Quadrant>::Element *_create_quadrant(const PosKey &p_qk);
void _erase_quadrant(Map<PosKey, Quadrant>::Element *Q);
void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true);
@@ -218,6 +236,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
virtual void _changed_callback(Object *p_changed, const char *p_prop);
public:
@@ -271,6 +290,9 @@ public:
void set_collision_use_kinematic(bool p_use_kinematic);
bool get_collision_use_kinematic() const;
+ void set_collision_use_parent(bool p_use_parent);
+ bool get_collision_use_parent() const;
+
void set_collision_friction(float p_friction);
float get_collision_friction() const;
@@ -298,6 +320,12 @@ public:
void set_y_sort_mode(bool p_enable);
bool is_y_sort_mode_enabled() const;
+ void set_compatibility_mode(bool p_enable);
+ bool is_compatibility_mode_enabled() const;
+
+ void set_centered_textures(bool p_enable);
+ bool is_centered_textures_enabled() const;
+
Array get_used_cells() const;
Array get_used_cells_by_id(int p_id) const;
Rect2 get_used_rect(); // Not const because of cache
@@ -314,6 +342,8 @@ public:
void set_clip_uv(bool p_enable);
bool get_clip_uv() const;
+ String get_configuration_warning() const;
+
void fix_invalid_tiles();
void clear();
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 1cf037daf2..a1d074e6cd 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -327,7 +327,7 @@ void VisibilityEnabler2D::_node_removed(Node *p_node) {
String VisibilityEnabler2D::get_configuration_warning() const {
#ifdef TOOLS_ENABLED
if (is_inside_tree() && get_parent() && (get_parent()->get_filename() == String() && get_parent() != get_tree()->get_edited_scene_root())) {
- return TTR("VisibilityEnable2D works best when used with the edited scene root directly as parent.");
+ return TTR("VisibilityEnabler2D works best when used with the edited scene root directly as parent.");
}
#endif
return String();