diff options
Diffstat (limited to 'scene/2d')
| -rw-r--r-- | scene/2d/animated_sprite.cpp | 9 | ||||
| -rw-r--r-- | scene/2d/animated_sprite.h | 16 | ||||
| -rw-r--r-- | scene/2d/area_2d.cpp | 54 | ||||
| -rw-r--r-- | scene/2d/area_2d.h | 10 | ||||
| -rw-r--r-- | scene/2d/audio_stream_player_2d.cpp | 463 | ||||
| -rw-r--r-- | scene/2d/audio_stream_player_2d.h | 96 | ||||
| -rw-r--r-- | scene/2d/camera_2d.cpp | 132 | ||||
| -rw-r--r-- | scene/2d/camera_2d.h | 13 | ||||
| -rw-r--r-- | scene/2d/canvas_item.cpp | 267 | ||||
| -rw-r--r-- | scene/2d/canvas_item.h | 106 | ||||
| -rw-r--r-- | scene/2d/collision_object_2d.cpp | 339 | ||||
| -rw-r--r-- | scene/2d/collision_object_2d.h | 62 | ||||
| -rw-r--r-- | scene/2d/collision_polygon_2d.cpp | 186 | ||||
| -rw-r--r-- | scene/2d/collision_polygon_2d.h | 32 | ||||
| -rw-r--r-- | scene/2d/collision_shape_2d.cpp | 176 | ||||
| -rw-r--r-- | scene/2d/collision_shape_2d.h | 26 | ||||
| -rw-r--r-- | scene/2d/line_2d.cpp | 3 | ||||
| -rw-r--r-- | scene/2d/particles_2d.cpp | 1191 | ||||
| -rw-r--r-- | scene/2d/particles_2d.h | 251 | ||||
| -rw-r--r-- | scene/2d/physics_body_2d.cpp | 452 | ||||
| -rw-r--r-- | scene/2d/physics_body_2d.h | 86 | ||||
| -rw-r--r-- | scene/2d/sprite.cpp | 41 | ||||
| -rw-r--r-- | scene/2d/sprite.h | 8 | ||||
| -rw-r--r-- | scene/2d/tile_map.cpp | 19 |
24 files changed, 2050 insertions, 1988 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index eba638b0f5..a1ab51b3c8 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -30,7 +30,8 @@ #include "animated_sprite.h" #include "os/os.h" #include "scene/scene_string_names.h" -#include "scene/scene_string_names.h" + +#define NORMAL_SUFFIX "_normal" //////////////////////////// @@ -82,6 +83,7 @@ void SpriteFrames::add_animation(const StringName &p_anim) { ERR_FAIL_COND(animations.has(p_anim)); animations[p_anim] = Anim(); + animations[p_anim].normal_name = String(p_anim) + NORMAL_SUFFIX; } bool SpriteFrames::has_animation(const StringName &p_anim) const { @@ -101,6 +103,7 @@ void SpriteFrames::rename_animation(const StringName &p_prev, const StringName & Anim anim = animations[p_prev]; animations.erase(p_prev); animations[p_next] = anim; + animations[p_next].normal_name = String(p_next) + NORMAL_SUFFIX; } Vector<String> SpriteFrames::_get_animation_list() const { @@ -374,6 +377,8 @@ void AnimatedSprite::_notification(int p_what) { return; } + Ref<Texture> normal = frames->get_normal_frame(animation, frame); + //print_line("DECIDED TO DRAW"); RID ci = get_canvas_item(); @@ -400,7 +405,7 @@ void AnimatedSprite::_notification(int p_what) { dst_rect.size.y = -dst_rect.size.y; //texture->draw_rect(ci,dst_rect,false,modulate); - texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size())); + texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size()), Color(1, 1, 1), false, normal); //VisualServer::get_singleton()->canvas_item_add_texture_rect_region(ci,dst_rect,texture->get_rid(),src_rect,modulate); } break; diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 64794b0ca3..80defac079 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -47,6 +47,8 @@ class SpriteFrames : public Resource { loop = true; speed = 5; } + + StringName normal_name; }; Map<StringName, Anim> animations; @@ -89,6 +91,20 @@ public: return E->get().frames[p_idx]; } + _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(p_idx < 0, Ref<Texture>()); + + const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name); + + if (!EN || p_idx >= EN->get().frames.size()) + return Ref<Texture>(); + + return EN->get().frames[p_idx]; + } + 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); diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index aca7a8c736..db22a38cec 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "area_2d.h" #include "scene/scene_string_names.h" +#include "servers/audio_server.h" #include "servers/physics_2d_server.h" + void Area2D::set_space_override_mode(SpaceOverride p_mode) { space_override = p_mode; @@ -542,6 +544,47 @@ bool Area2D::get_collision_layer_bit(int p_bit) const { return get_collision_layer() & (1 << p_bit); } +void Area2D::set_audio_bus_override(bool p_override) { + + audio_bus_override = p_override; +} + +bool Area2D::is_overriding_audio_bus() const { + + return audio_bus_override; +} + +void Area2D::set_audio_bus(const StringName &p_audio_bus) { + + audio_bus = p_audio_bus; +} + +StringName Area2D::get_audio_bus() const { + + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (AudioServer::get_singleton()->get_bus_name(i) == audio_bus) { + return audio_bus; + } + } + return "Master"; +} + +void Area2D::_validate_property(PropertyInfo &property) const { + + if (property.name == "audio_bus_name") { + + String options; + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (i > 0) + options += ","; + String name = AudioServer::get_singleton()->get_bus_name(i); + options += name; + } + + property.hint_string = options; + } +} + void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_body_enter_tree", "id"), &Area2D::_body_enter_tree); @@ -598,6 +641,12 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("overlaps_body", "body"), &Area2D::overlaps_body); ClassDB::bind_method(D_METHOD("overlaps_area", "area"), &Area2D::overlaps_area); + ClassDB::bind_method(D_METHOD("set_audio_bus", "name"), &Area2D::set_audio_bus); + ClassDB::bind_method(D_METHOD("get_audio_bus"), &Area2D::get_audio_bus); + + ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area2D::set_audio_bus_override); + ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area2D::is_overriding_audio_bus); + ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout); ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout); @@ -624,6 +673,10 @@ void Area2D::_bind_methods() { ADD_GROUP("Collision", "collision_"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_GROUP("Audio Bus", "audio_bus_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus", "get_audio_bus"); } Area2D::Area2D() @@ -642,6 +695,7 @@ Area2D::Area2D() monitorable = false; collision_mask = 1; collision_layer = 1; + audio_bus_override = false; set_monitoring(true); set_monitorable(true); } diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index f0eb1e9240..12d71f3911 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -126,9 +126,13 @@ private: Map<ObjectID, AreaState> area_map; void _clear_monitoring(); + bool audio_bus_override; + StringName audio_bus; + protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_space_override_mode(SpaceOverride p_mode); @@ -179,6 +183,12 @@ public: bool overlaps_area(Node *p_area) const; bool overlaps_body(Node *p_body) const; + void set_audio_bus_override(bool p_override); + bool is_overriding_audio_bus() const; + + void set_audio_bus(const StringName &p_audio_bus); + StringName get_audio_bus() const; + Area2D(); ~Area2D(); }; diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp new file mode 100644 index 0000000000..cef473dcdf --- /dev/null +++ b/scene/2d/audio_stream_player_2d.cpp @@ -0,0 +1,463 @@ + +#include "audio_stream_player_2d.h" +#include "scene/2d/area_2d.h" +#include "scene/main/viewport.h" +void AudioStreamPlayer2D::_mix_audio() { + + if (!stream_playback.is_valid()) { + return; + } + + if (!active) { + return; + } + + if (setseek >= 0.0) { + stream_playback->start(setseek); + setseek = -1.0; //reset seek + } + + //get data + AudioFrame *buffer = mix_buffer.ptr(); + int buffer_size = mix_buffer.size(); + + //mix + stream_playback->mix(buffer, 1.0, buffer_size); + + //write all outputs + for (int i = 0; i < output_count; i++) { + + Output current = outputs[i]; + + //see if current output exists, to keep volume ramp + bool found = false; + for (int j = i; j < prev_output_count; j++) { + if (prev_outputs[j].viewport == current.viewport) { + if (j != i) { + SWAP(prev_outputs[j], prev_outputs[i]); + } + found = true; + break; + } + } + + if (!found) { + //create new if was not used before + if (prev_output_count < MAX_OUTPUTS) { + prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport + prev_output_count++; + } + prev_outputs[i] = current; + } + + //mix! + AudioFrame vol_inc = (current.vol - prev_outputs[i].vol) / float(buffer_size); + AudioFrame vol = current.vol; + + switch (AudioServer::get_singleton()->get_speaker_mode()) { + + case AudioServer::SPEAKER_MODE_STEREO: { + AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 0); + + for (int j = 0; j < buffer_size; j++) { + + target[j] += buffer[j] * vol; + vol += vol_inc; + } + + } break; + case AudioServer::SPEAKER_SURROUND_51: { + + AudioFrame *targets[2] = { + AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 1), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 2), + }; + + for (int j = 0; j < buffer_size; j++) { + + AudioFrame frame = buffer[j] * vol; + targets[0][j] += frame; + targets[1][j] += frame; + vol += vol_inc; + } + + } break; + case AudioServer::SPEAKER_SURROUND_71: { + + AudioFrame *targets[3] = { + AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 1), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 2), + AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, 3) + }; + + for (int j = 0; j < buffer_size; j++) { + + AudioFrame frame = buffer[j] * vol; + targets[0][j] += frame; + targets[1][j] += frame; + targets[2][j] += frame; + vol += vol_inc; + } + + } break; + } + + prev_outputs[i] = current; + } + + prev_output_count = output_count; + + //stream is no longer active, disable this. + if (!stream_playback->is_playing()) { + active = false; + } + + output_ready = false; +} + +void AudioStreamPlayer2D::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + AudioServer::get_singleton()->add_callback(_mix_audios, this); + if (autoplay && !get_tree()->is_editor_hint()) { + play(); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + + AudioServer::get_singleton()->remove_callback(_mix_audios, this); + } + + if (p_what == NOTIFICATION_INTERNAL_FIXED_PROCESS) { + + //update anything related to position first, if possible of course + + if (!output_ready) { + List<Viewport *> viewports; + Ref<World2D> world_2d = get_world_2d(); + ERR_FAIL_COND(world_2d.is_null()); + + int new_output_count = 0; + + Vector2 global_pos = get_global_position(); + + int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + + //check if any area is diverting sound into a bus + + Physics2DDirectSpaceState *space_state = Physics2DServer::get_singleton()->space_get_direct_state(world_2d->get_space()); + + Physics2DDirectSpaceState::ShapeResult sr[MAX_INTERSECT_AREAS]; + + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, Physics2DDirectSpaceState::TYPE_MASK_AREA); + + for (int i = 0; i < areas; i++) { + if (!sr[i].collider) + continue; + + Area2D *area2d = sr[i].collider->cast_to<Area2D>(); + if (!area2d) + continue; + + if (!area2d->is_overriding_audio_bus()) + continue; + + StringName bus_name = area2d->get_audio_bus(); + bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); + break; + } + + world_2d->get_viewport_list(&viewports); + for (List<Viewport *>::Element *E = viewports.front(); E; E = E->next()) { + + Viewport *vp = E->get(); + if (vp->is_audio_listener_2d()) { + + //compute matrix to convert to screen + Transform2D to_screen = vp->get_global_canvas_transform() * vp->get_canvas_transform(); + Vector2 screen_size = vp->get_visible_rect().size; + + //screen in global is used for attenuation + Vector2 screen_in_global = to_screen.affine_inverse().xform(screen_size * 0.5); + + float dist = global_pos.distance_to(screen_in_global); //distance to screen center + + if (dist > max_distance) + continue; //cant hear this sound in this viewport + + float multiplier = Math::pow(1.0f - dist / max_distance, attenuation); + multiplier *= Math::db2linear(volume_db); //also apply player volume! + + //point in screen is used for panning + Vector2 point_in_screen = to_screen.xform(global_pos); + + float pan = CLAMP(point_in_screen.x / screen_size.width, 0.0, 1.0); + + float l = 1.0 - pan; + float r = pan; + + outputs[new_output_count].vol = AudioFrame(l, r) * multiplier; + outputs[new_output_count].bus_index = bus_index; + outputs[new_output_count].viewport = vp; //keep pointer only for reference + new_output_count++; + if (new_output_count == MAX_OUTPUTS) + break; + } + } + + output_count = new_output_count; + output_ready = true; + } + + //start playing if requested + if (setplay >= 0.0) { + setseek = setplay; + active = true; + setplay = -1; + _change_notify("playing"); //update property in editor + } + + //stop playing if no longer active + if (!active) { + set_fixed_process_internal(false); + _change_notify("playing"); //update property in editor + } + } +} + +void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) { + + ERR_FAIL_COND(!p_stream.is_valid()); + AudioServer::get_singleton()->lock(); + + mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); + + if (stream_playback.is_valid()) { + stream_playback.unref(); + stream.unref(); + active = false; + setseek = -1; + } + + stream = p_stream; + stream_playback = p_stream->instance_playback(); + + if (stream_playback.is_null()) { + stream.unref(); + ERR_FAIL_COND(stream_playback.is_null()); + } + + AudioServer::get_singleton()->unlock(); +} + +Ref<AudioStream> AudioStreamPlayer2D::get_stream() const { + + return stream; +} + +void AudioStreamPlayer2D::set_volume_db(float p_volume) { + + volume_db = p_volume; +} +float AudioStreamPlayer2D::get_volume_db() const { + + return volume_db; +} + +void AudioStreamPlayer2D::play(float p_from_pos) { + + if (stream_playback.is_valid()) { + setplay = p_from_pos; + output_ready = false; + set_fixed_process_internal(true); + } +} + +void AudioStreamPlayer2D::seek(float p_seconds) { + + if (stream_playback.is_valid()) { + setseek = p_seconds; + } +} + +void AudioStreamPlayer2D::stop() { + + if (stream_playback.is_valid()) { + active = false; + set_fixed_process_internal(false); + setplay = -1; + } +} + +bool AudioStreamPlayer2D::is_playing() const { + + if (stream_playback.is_valid()) { + return active; // && stream_playback->is_playing(); + } + + return false; +} + +float AudioStreamPlayer2D::get_pos() { + + if (stream_playback.is_valid()) { + return stream_playback->get_pos(); + } + + return 0; +} + +void AudioStreamPlayer2D::set_bus(const StringName &p_bus) { + + //if audio is active, must lock this + AudioServer::get_singleton()->lock(); + bus = p_bus; + AudioServer::get_singleton()->unlock(); +} +StringName AudioStreamPlayer2D::get_bus() const { + + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (AudioServer::get_singleton()->get_bus_name(i) == bus) { + return bus; + } + } + return "Master"; +} + +void AudioStreamPlayer2D::set_autoplay(bool p_enable) { + + autoplay = p_enable; +} +bool AudioStreamPlayer2D::is_autoplay_enabled() { + + return autoplay; +} + +void AudioStreamPlayer2D::_set_playing(bool p_enable) { + + if (p_enable) + play(); + else + stop(); +} +bool AudioStreamPlayer2D::_is_active() const { + + return active; +} + +void AudioStreamPlayer2D::_validate_property(PropertyInfo &property) const { + + if (property.name == "bus") { + + String options; + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { + if (i > 0) + options += ","; + String name = AudioServer::get_singleton()->get_bus_name(i); + options += name; + } + + property.hint_string = options; + } +} + +void AudioStreamPlayer2D::_bus_layout_changed() { + + _change_notify(); +} + +void AudioStreamPlayer2D::set_max_distance(float p_pixels) { + + ERR_FAIL_COND(p_pixels <= 0.0); + max_distance = p_pixels; +} + +float AudioStreamPlayer2D::get_max_distance() const { + + return max_distance; +} + +void AudioStreamPlayer2D::set_attenuation(float p_curve) { + + attenuation = p_curve; +} +float AudioStreamPlayer2D::get_attenuation() const { + + return attenuation; +} + +void AudioStreamPlayer2D::set_area_mask(uint32_t p_mask) { + + area_mask = p_mask; +} + +uint32_t AudioStreamPlayer2D::get_area_mask() const { + + return area_mask; +} + +void AudioStreamPlayer2D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_stream", "stream:AudioStream"), &AudioStreamPlayer2D::set_stream); + ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream); + + ClassDB::bind_method(D_METHOD("set_volume_db", "volume_db"), &AudioStreamPlayer2D::set_volume_db); + ClassDB::bind_method(D_METHOD("get_volume_db"), &AudioStreamPlayer2D::get_volume_db); + + ClassDB::bind_method(D_METHOD("play", "from_pos"), &AudioStreamPlayer2D::play, DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("seek", "to_pos"), &AudioStreamPlayer2D::seek); + ClassDB::bind_method(D_METHOD("stop"), &AudioStreamPlayer2D::stop); + + ClassDB::bind_method(D_METHOD("is_playing"), &AudioStreamPlayer2D::is_playing); + ClassDB::bind_method(D_METHOD("get_pos"), &AudioStreamPlayer2D::get_pos); + + ClassDB::bind_method(D_METHOD("set_bus", "bus"), &AudioStreamPlayer2D::set_bus); + ClassDB::bind_method(D_METHOD("get_bus"), &AudioStreamPlayer2D::get_bus); + + ClassDB::bind_method(D_METHOD("set_autoplay", "enable"), &AudioStreamPlayer2D::set_autoplay); + ClassDB::bind_method(D_METHOD("is_autoplay_enabled"), &AudioStreamPlayer2D::is_autoplay_enabled); + + ClassDB::bind_method(D_METHOD("_set_playing", "enable"), &AudioStreamPlayer2D::_set_playing); + ClassDB::bind_method(D_METHOD("_is_active"), &AudioStreamPlayer2D::_is_active); + + ClassDB::bind_method(D_METHOD("set_max_distance", "pixels"), &AudioStreamPlayer2D::set_max_distance); + ClassDB::bind_method(D_METHOD("get_max_distance"), &AudioStreamPlayer2D::get_max_distance); + + ClassDB::bind_method(D_METHOD("set_attenuation", "curve"), &AudioStreamPlayer2D::set_attenuation); + ClassDB::bind_method(D_METHOD("get_attenuation"), &AudioStreamPlayer2D::get_attenuation); + + ClassDB::bind_method(D_METHOD("set_area_mask", "mask"), &AudioStreamPlayer2D::set_area_mask); + ClassDB::bind_method(D_METHOD("get_area_mask"), &AudioStreamPlayer2D::get_area_mask); + + 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"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_db", PROPERTY_HINT_RANGE, "-80,24"), "set_volume_db", "get_volume_db"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "_is_active"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING), "set_attenuation", "get_attenuation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); +} + +AudioStreamPlayer2D::AudioStreamPlayer2D() { + + volume_db = 0; + autoplay = false; + setseek = -1; + active = false; + output_count = 0; + prev_output_count = 0; + max_distance = 2000; + attenuation = 1; + setplay = -1; + output_ready = false; + area_mask = 1; + AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); +} + +AudioStreamPlayer2D::~AudioStreamPlayer2D() { +} diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h new file mode 100644 index 0000000000..25eff61b76 --- /dev/null +++ b/scene/2d/audio_stream_player_2d.h @@ -0,0 +1,96 @@ +#ifndef AUDIO_STREAM_PLAYER_2D_H +#define AUDIO_STREAM_PLAYER_2D_H + +#include "scene/2d/node_2d.h" +#include "servers/audio/audio_stream.h" +#include "servers/audio_server.h" + +class AudioStreamPlayer2D : public Node2D { + + GDCLASS(AudioStreamPlayer2D, Node2D) + +private: + enum { + MAX_OUTPUTS = 8, + MAX_INTERSECT_AREAS = 32 + + }; + + struct Output { + + AudioFrame vol; + int bus_index; + Viewport *viewport; //pointer only used for reference to previous mix + }; + + Output outputs[MAX_OUTPUTS]; + volatile int output_count; + volatile bool output_ready; + + //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) + Output prev_outputs[MAX_OUTPUTS]; + int prev_output_count; + + Ref<AudioStreamPlayback> stream_playback; + Ref<AudioStream> stream; + Vector<AudioFrame> mix_buffer; + + volatile float setseek; + volatile bool active; + volatile float setplay; + + float volume_db; + bool autoplay; + StringName bus; + + void _mix_audio(); + static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer2D *>(self)->_mix_audio(); } + + void _set_playing(bool p_enable); + bool _is_active() const; + + void _bus_layout_changed(); + + uint32_t area_mask; + + float max_distance; + float attenuation; + +protected: + void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_stream(Ref<AudioStream> p_stream); + Ref<AudioStream> get_stream() const; + + void set_volume_db(float p_volume); + float get_volume_db() const; + + void play(float p_from_pos = 0.0); + void seek(float p_seconds); + void stop(); + bool is_playing() const; + float get_pos(); + + void set_bus(const StringName &p_bus); + StringName get_bus() const; + + void set_autoplay(bool p_enable); + bool is_autoplay_enabled(); + + void set_max_distance(float p_pixels); + float get_max_distance() const; + + void set_attenuation(float p_curve); + float get_attenuation() const; + + void set_area_mask(uint32_t p_mask); + uint32_t get_area_mask() const; + + AudioStreamPlayer2D(); + ~AudioStreamPlayer2D(); +}; + +#endif diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 9d8b5da366..908c95b50c 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -199,10 +199,8 @@ Transform2D Camera2D::get_camera_transform() { /* if (0) { - xform = get_global_transform() * xform; } else { - xform.elements[2]+=get_global_transform().get_origin(); } */ @@ -267,25 +265,76 @@ void Camera2D::_notification(int p_what) { if (!is_inside_tree() || !get_tree()->is_editor_hint()) break; - Color area_axis_color(0.5, 0.42, 0.87, 0.63); - float area_axis_width = 1; - if (current) - area_axis_width = 3; + if (screen_drawing_enabled) { + Color area_axis_color(0.5, 0.42, 0.87, 0.63); + float area_axis_width = 1; + if (is_current()) { + area_axis_width = 3; + area_axis_color.a = 0.83; + } - Transform2D inv_camera_transform = get_camera_transform().affine_inverse(); - Size2 screen_size = get_viewport_rect().size; + Transform2D inv_camera_transform = get_camera_transform().affine_inverse(); + Size2 screen_size = get_viewport_rect().size; - Vector2 screen_endpoints[4] = { - inv_camera_transform.xform(Vector2(0, 0)), - inv_camera_transform.xform(Vector2(screen_size.width, 0)), - inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)), - inv_camera_transform.xform(Vector2(0, screen_size.height)) - }; + Vector2 screen_endpoints[4] = { + inv_camera_transform.xform(Vector2(0, 0)), + inv_camera_transform.xform(Vector2(screen_size.width, 0)), + inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)), + inv_camera_transform.xform(Vector2(0, screen_size.height)) + }; - Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space + Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space - for (int i = 0; i < 4; i++) { - draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width); + for (int i = 0; i < 4; i++) { + draw_line(inv_transform.xform(screen_endpoints[i]), inv_transform.xform(screen_endpoints[(i + 1) % 4]), area_axis_color, area_axis_width); + } + } + + if (limit_drawing_enabled) { + Color limit_drawing_color(1, 1, 0, 0.63); + float limit_drawing_width = 1; + if (is_current()) { + limit_drawing_color.a = 0.83; + limit_drawing_width = 3; + } + + Vector2 camera_origin = get_global_transform().get_origin(); + Vector2 camera_scale = get_global_transform().get_scale().abs(); + Vector2 limit_points[4] = { + (Vector2(limit[MARGIN_LEFT], limit[MARGIN_TOP]) - camera_origin) / camera_scale, + (Vector2(limit[MARGIN_RIGHT], limit[MARGIN_TOP]) - camera_origin) / camera_scale, + (Vector2(limit[MARGIN_RIGHT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale, + (Vector2(limit[MARGIN_LEFT], limit[MARGIN_BOTTOM]) - camera_origin) / camera_scale + }; + + for (int i = 0; i < 4; i++) { + draw_line(limit_points[i], limit_points[(i + 1) % 4], limit_drawing_color, limit_drawing_width); + } + } + + if (margin_drawing_enabled) { + Color margin_drawing_color(0, 1, 1, 0.63); + float margin_drawing_width = 1; + if (is_current()) { + margin_drawing_width = 3; + margin_drawing_color.a = 0.83; + } + + Transform2D inv_camera_transform = get_camera_transform().affine_inverse(); + Size2 screen_size = get_viewport_rect().size; + + Vector2 margin_endpoints[4] = { + inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) - ((screen_size.height / 2) * drag_margin[MARGIN_TOP]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) + ((screen_size.width / 2) * drag_margin[MARGIN_RIGHT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM]))), + inv_camera_transform.xform(Vector2((screen_size.width / 2) - ((screen_size.width / 2) * drag_margin[MARGIN_LEFT]), (screen_size.height / 2) + ((screen_size.height / 2) * drag_margin[MARGIN_BOTTOM]))) + }; + + Transform2D inv_transform = get_global_transform().affine_inverse(); // undo global space + + for (int i = 0; i < 4; i++) { + draw_line(inv_transform.xform(margin_endpoints[i]), inv_transform.xform(margin_endpoints[(i + 1) % 4]), margin_drawing_color, margin_drawing_width); + } } } break; @@ -330,7 +379,6 @@ void Camera2D::_make_current(Object *p_which) { if (p_which == this) { current = true; - _update_scroll(); } else { current = false; } @@ -342,6 +390,7 @@ void Camera2D::_set_current(bool p_current) { make_current(); current = p_current; + update(); } bool Camera2D::is_current() const { @@ -370,6 +419,7 @@ void Camera2D::set_limit(Margin p_margin, int p_limit) { ERR_FAIL_INDEX(p_margin, 4); limit[p_margin] = p_limit; + update(); } int Camera2D::get_limit(Margin p_margin) const { @@ -393,6 +443,7 @@ void Camera2D::set_drag_margin(Margin p_margin, float p_drag_margin) { ERR_FAIL_INDEX(p_margin, 4); drag_margin[p_margin] = p_drag_margin; + update(); } float Camera2D::get_drag_margin(Margin p_margin) const { @@ -554,6 +605,33 @@ Node *Camera2D::get_custom_viewport() const { return custom_viewport; } +void Camera2D::set_screen_drawing_enabled(bool enable) { + screen_drawing_enabled = enable; + update(); +} + +bool Camera2D::is_screen_drawing_enabled() const { + return screen_drawing_enabled; +} + +void Camera2D::set_limit_drawing_enabled(bool enable) { + limit_drawing_enabled = enable; + update(); +} + +bool Camera2D::is_limit_drawing_enabled() const { + return limit_drawing_enabled; +} + +void Camera2D::set_margin_drawing_enabled(bool enable) { + margin_drawing_enabled = enable; + update(); +} + +bool Camera2D::is_margin_drawing_enabled() const { + return margin_drawing_enabled; +} + void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Camera2D::set_offset); @@ -616,6 +694,15 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_old_smoothing", "follow_smoothing"), &Camera2D::_set_old_smoothing); + ClassDB::bind_method(D_METHOD("set_screen_drawing_enabled", "screen_drawing_enabled"), &Camera2D::set_screen_drawing_enabled); + ClassDB::bind_method(D_METHOD("is_screen_drawing_enabled"), &Camera2D::is_screen_drawing_enabled); + + ClassDB::bind_method(D_METHOD("set_limit_drawing_enabled", "limit_drawing_enabled"), &Camera2D::set_limit_drawing_enabled); + ClassDB::bind_method(D_METHOD("is_limit_drawing_enabled"), &Camera2D::is_limit_drawing_enabled); + + ClassDB::bind_method(D_METHOD("set_margin_drawing_enabled", "margin_drawing_enabled"), &Camera2D::set_margin_drawing_enabled); + ClassDB::bind_method(D_METHOD("is_margin_drawing_enabled"), &Camera2D::is_margin_drawing_enabled); + ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotating"), "set_rotating", "is_rotating"); @@ -643,6 +730,11 @@ void Camera2D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_right", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "drag_margin_bottom", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_margin", "get_drag_margin", MARGIN_BOTTOM); + ADD_GROUP("Editor", "editor_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_screen"), "set_screen_drawing_enabled", "is_screen_drawing_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_limits"), "set_limit_drawing_enabled", "is_limit_drawing_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_draw_drag_margin"), "set_margin_drawing_enabled", "is_margin_drawing_enabled"); + BIND_CONSTANT(ANCHOR_MODE_DRAG_CENTER); BIND_CONSTANT(ANCHOR_MODE_FIXED_TOP_LEFT); } @@ -671,6 +763,10 @@ Camera2D::Camera2D() { smoothing = 5.0; zoom = Vector2(1, 1); + screen_drawing_enabled = true; + limit_drawing_enabled = false; + margin_drawing_enabled = false; + h_drag_enabled = true; v_drag_enabled = true; h_ofs = 0; diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 686f40bedf..8d9e76be85 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -79,6 +79,10 @@ protected: void _set_old_smoothing(float p_enable); + bool screen_drawing_enabled; + bool limit_drawing_enabled; + bool margin_drawing_enabled; + protected: virtual Transform2D get_camera_transform(); void _notification(int p_what); @@ -138,6 +142,15 @@ public: void reset_smoothing(); void align(); + void set_screen_drawing_enabled(bool enable); + bool is_screen_drawing_enabled() const; + + void set_limit_drawing_enabled(bool enable); + bool is_limit_drawing_enabled() const; + + void set_margin_drawing_enabled(bool enable); + bool is_margin_drawing_enabled() const; + Camera2D(); }; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index b2258ec94b..4a80aba355 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "canvas_item.h" +#include "core/method_bind_ext.gen.inc" #include "message_queue.h" #include "os/input.h" #include "scene/main/canvas_layer.h" @@ -38,6 +39,196 @@ #include "scene/scene_string_names.h" #include "servers/visual_server.h" +Mutex *CanvasItemMaterial::material_mutex = NULL; +SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials; +Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map; + +void CanvasItemMaterial::init_shaders() { + +#ifndef NO_THREADS + material_mutex = Mutex::create(); +#endif +} + +void CanvasItemMaterial::finish_shaders() { + +#ifndef NO_THREADS + memdelete(material_mutex); +#endif +} + +void CanvasItemMaterial::_update_shader() { + + dirty_materials.remove(&element); + + MaterialKey mk = _compute_key(); + if (mk.key == current_key.key) + return; //no update required in the end + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + } + + current_key = mk; + + if (shader_map.has(mk)) { + + VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader); + shader_map[mk].users++; + return; + } + + //must create a shader! + + String code = "shader_type canvas_item;\nrender_mode "; + switch (blend_mode) { + case BLEND_MODE_MIX: code += "blend_mix"; break; + case BLEND_MODE_ADD: code += "blend_add"; break; + case BLEND_MODE_SUB: code += "blend_sub"; break; + case BLEND_MODE_MUL: code += "blend_mul"; break; + case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break; + } + + switch (light_mode) { + case LIGHT_MODE_NORMAL: break; + case LIGHT_MODE_UNSHADED: code += ",unshaded"; break; + case LIGHT_MODE_LIGHT_ONLY: code += ",light_only"; break; + } + code += ";\n"; //thats it. + + ShaderData shader_data; + shader_data.shader = VS::get_singleton()->shader_create(); + shader_data.users = 1; + + VS::get_singleton()->shader_set_code(shader_data.shader, code); + + shader_map[mk] = shader_data; + + VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader); +} + +void CanvasItemMaterial::flush_changes() { + + if (material_mutex) + material_mutex->lock(); + + while (dirty_materials.first()) { + + dirty_materials.first()->self()->_update_shader(); + } + + if (material_mutex) + material_mutex->unlock(); +} + +void CanvasItemMaterial::_queue_shader_change() { + + if (material_mutex) + material_mutex->lock(); + + if (!element.in_list()) { + dirty_materials.add(&element); + } + + if (material_mutex) + material_mutex->unlock(); +} + +bool CanvasItemMaterial::_is_shader_dirty() const { + + bool dirty = false; + + if (material_mutex) + material_mutex->lock(); + + dirty = element.in_list(); + + if (material_mutex) + material_mutex->unlock(); + + return dirty; +} +void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) { + + blend_mode = p_blend_mode; + _queue_shader_change(); +} + +CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const { + return blend_mode; +} + +void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) { + + light_mode = p_light_mode; + _queue_shader_change(); +} + +CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const { + + return light_mode; +} + +void CanvasItemMaterial::_validate_property(PropertyInfo &property) const { +} + +void CanvasItemMaterial::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode); + ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode); + + ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode); + ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode"); + + BIND_CONSTANT(BLEND_MODE_MIX); + BIND_CONSTANT(BLEND_MODE_ADD); + BIND_CONSTANT(BLEND_MODE_SUB); + BIND_CONSTANT(BLEND_MODE_MUL); + BIND_CONSTANT(BLEND_MODE_PREMULT_ALPHA); + BIND_CONSTANT(LIGHT_MODE_NORMAL); + BIND_CONSTANT(LIGHT_MODE_UNSHADED); + BIND_CONSTANT(LIGHT_MODE_LIGHT_ONLY); +} + +CanvasItemMaterial::CanvasItemMaterial() + : element(this) { + + blend_mode = BLEND_MODE_MIX; + light_mode = LIGHT_MODE_NORMAL; + + current_key.key = 0; + current_key.invalid_key = 1; + _queue_shader_change(); +} + +CanvasItemMaterial::~CanvasItemMaterial() { + + if (material_mutex) + material_mutex->lock(); + + if (shader_map.has(current_key)) { + shader_map[current_key].users--; + if (shader_map[current_key].users == 0) { + //deallocate shader, as it's no longer in use + VS::get_singleton()->free(shader_map[current_key].shader); + shader_map.erase(current_key); + } + + VS::get_singleton()->material_set_shader(_get_material(), RID()); + } + + if (material_mutex) + material_mutex->unlock(); +} + /////////////////////////////////////////////////////////////////// bool CanvasItem::is_visible_in_tree() const { @@ -176,7 +367,9 @@ Transform2D CanvasItem::get_global_transform_with_canvas() const { } Transform2D CanvasItem::get_global_transform() const { - +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V(!is_inside_tree(), get_transform()); +#endif if (global_invalid) { const CanvasItem *pi = get_parent_item(); @@ -416,14 +609,22 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased); } -void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color) { +void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) { if (!drawing) { ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL(); } - VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color); + if (p_filled) { + + 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); + } } void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) { @@ -436,7 +637,7 @@ void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p 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) { +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."); @@ -448,7 +649,7 @@ void CanvasItem::draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos p_texture->draw(canvas_item, p_pos, p_modulate); } -void CanvasItem::draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose) { +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."); @@ -456,16 +657,16 @@ void CanvasItem::draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p } ERR_FAIL_COND(p_texture.is_null()); - p_texture->draw_rect(canvas_item, p_rect, p_tile, p_modulate, p_transpose); + 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) { +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(p_texture.is_null()); - p_texture->draw_rect_region(canvas_item, p_rect, p_src_rect, p_modulate, p_transpose); + 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) { @@ -478,7 +679,7 @@ void CanvasItem::draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p p_style_box->draw(canvas_item, p_rect); } -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) { +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."); @@ -486,8 +687,9 @@ void CanvasItem::draw_primitive(const Vector<Point2> &p_points, const Vector<Col } 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(); - VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width); + VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item, p_points, p_colors, p_uvs, rid, p_width, rid_normal); } void CanvasItem::draw_set_transform(const Point2 &p_offset, float p_rot, const Size2 &p_scale) { @@ -511,7 +713,7 @@ void CanvasItem::draw_set_transform_matrix(const Transform2D &p_matrix) { 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) { +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) { if (!drawing) { ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -519,11 +721,12 @@ void CanvasItem::draw_polygon(const Vector<Point2> &p_points, const Vector<Color } 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(); - VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid); + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, p_colors, p_uvs, rid, rid_normal); } -void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs, Ref<Texture> p_texture) { +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) { if (!drawing) { ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); @@ -533,8 +736,9 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo Vector<Color> colors; colors.push_back(p_color); 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(); - VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid); + VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal); } 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) { @@ -563,8 +767,9 @@ float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const void CanvasItem::_notify_transform(CanvasItem *p_node) { - if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) + if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) { return; //nothing to do + } p_node->global_invalid = true; @@ -653,7 +858,7 @@ bool CanvasItem::is_draw_behind_parent_enabled() const { return behind; } -void CanvasItem::set_material(const Ref<ShaderMaterial> &p_material) { +void CanvasItem::set_material(const Ref<Material> &p_material) { material = p_material; RID rid; @@ -674,7 +879,7 @@ bool CanvasItem::get_use_parent_material() const { return use_parent_material; } -Ref<ShaderMaterial> CanvasItem::get_material() const { +Ref<Material> CanvasItem::get_material() const { return material; } @@ -750,15 +955,15 @@ void CanvasItem::_bind_methods() { //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform); 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_rect", "rect", "color"), &CanvasItem::draw_rect); + ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true)); ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle); - ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1))); - ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture:Texture", "rect", "src_rect", "modulate", "transpose"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("draw_texture_rect_region", "texture:Texture", "rect", "src_rect", "modulate", "transpose", "normal_map:Texture", "clip_uv"), &CanvasItem::draw_texture_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("draw_style_box", "style_box:StyleBox", "rect"), &CanvasItem::draw_style_box); - ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture:Texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Variant()), DEFVAL(1.0)); - ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture:Texture"), &CanvasItem::draw_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture:Texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture:Texture", "width", "normal_map:Texture"), &CanvasItem::draw_primitive, DEFVAL(Variant()), DEFVAL(1.0), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture:Texture", "normal_map:Texture"), &CanvasItem::draw_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture:Texture", "normal_map:Texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("draw_string", "font:Font", "pos", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("draw_char", "font:Font", "pos", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1))); @@ -776,8 +981,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d); //ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport); - ClassDB::bind_method(D_METHOD("set_material", "material:ShaderMaterial"), &CanvasItem::set_material); - ClassDB::bind_method(D_METHOD("get_material:ShaderMaterial"), &CanvasItem::get_material); + ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &CanvasItem::set_material); + ClassDB::bind_method(D_METHOD("get_material:Material"), &CanvasItem::get_material); ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material); ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material); @@ -803,7 +1008,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTYNO(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); ADD_GROUP("Material", ""); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material"); //exporting these two things doesn't really make much sense i think //ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), "set_as_toplevel","is_set_as_toplevel") ; @@ -865,7 +1070,15 @@ bool CanvasItem::is_local_transform_notification_enabled() const { } void CanvasItem::set_notify_transform(bool p_enable) { + if (notify_transform == p_enable) + return; + notify_transform = p_enable; + + if (notify_transform && is_inside_tree()) { + //this ensures that invalid globals get resolved, so notifications can be received + get_global_transform(); + } } bool CanvasItem::is_transform_notification_enabled() const { diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index bea4301326..bffc171fc1 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -42,6 +42,92 @@ class Font; class StyleBox; +class CanvasItemMaterial : public Material { + + GDCLASS(CanvasItemMaterial, Material) + +public: + enum BlendMode { + BLEND_MODE_MIX, + BLEND_MODE_ADD, + BLEND_MODE_SUB, + BLEND_MODE_MUL, + BLEND_MODE_PREMULT_ALPHA + }; + + enum LightMode { + LIGHT_MODE_NORMAL, + LIGHT_MODE_UNSHADED, + LIGHT_MODE_LIGHT_ONLY + }; + +private: + union MaterialKey { + + struct { + uint32_t blend_mode : 4; + uint32_t light_mode : 4; + uint32_t invalid_key : 1; + }; + + uint32_t key; + + bool operator<(const MaterialKey &p_key) const { + return key < p_key.key; + } + }; + + struct ShaderData { + RID shader; + int users; + }; + + static Map<MaterialKey, ShaderData> shader_map; + + MaterialKey current_key; + + _FORCE_INLINE_ MaterialKey _compute_key() const { + + MaterialKey mk; + mk.key = 0; + mk.blend_mode = blend_mode; + mk.light_mode = light_mode; + return mk; + } + + static Mutex *material_mutex; + static SelfList<CanvasItemMaterial>::List dirty_materials; + SelfList<CanvasItemMaterial> element; + + void _update_shader(); + _FORCE_INLINE_ void _queue_shader_change(); + _FORCE_INLINE_ bool _is_shader_dirty() const; + + BlendMode blend_mode; + LightMode light_mode; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_blend_mode(BlendMode p_blend_mode); + BlendMode get_blend_mode() const; + + void set_light_mode(LightMode p_light_mode); + LightMode get_light_mode() const; + + static void init_shaders(); + static void finish_shaders(); + static void flush_changes(); + + CanvasItemMaterial(); + virtual ~CanvasItemMaterial(); +}; + +VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode) +VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode) + class CanvasItem : public Node { GDCLASS(CanvasItem, Node); @@ -83,7 +169,7 @@ private: bool notify_local_transform; bool notify_transform; - Ref<ShaderMaterial> material; + Ref<Material> material; mutable Transform2D global_transform; mutable bool global_invalid; @@ -156,15 +242,15 @@ public: /* DRAWING API */ 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_rect(const Rect2 &p_rect, const Color &p_color); + void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true); 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)); - 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); - void draw_texture_rect_region(const Ref<Texture> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false); + 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>()); + void draw_texture_rect_region(const Ref<Texture> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_clip_uv = true); void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect); - void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture> p_texture = Ref<Texture>(), float p_width = 1); - 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>()); - 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>()); + void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture> p_texture = Ref<Texture>(), float p_width = 1, const Ref<Texture> &p_normal_map = Ref<Texture>()); + 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>()); + 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>()); 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); float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1)); @@ -203,8 +289,8 @@ public: RID get_canvas() const; Ref<World2D> get_world_2d() const; - void set_material(const Ref<ShaderMaterial> &p_material); - Ref<ShaderMaterial> get_material() const; + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; void set_use_parent_material(bool p_use_parent_material); bool get_use_parent_material() const; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index caf9cf201a..c5c274e225 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -31,18 +31,6 @@ #include "scene/scene_string_names.h" #include "servers/physics_2d_server.h" -void CollisionObject2D::_update_shapes_from_children() { - - shapes.clear(); - for (int i = 0; i < get_child_count(); i++) { - - Node *n = get_child(i); - n->call("_add_to_collision_object", this); - } - - _update_shapes(); -} - void CollisionObject2D::_notification(int p_what) { switch (p_what) { @@ -88,82 +76,197 @@ void CollisionObject2D::_notification(int p_what) { } } -void CollisionObject2D::_update_shapes() { +uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) { - if (!rid.is_valid()) - return; + ShapeData sd; + uint32_t id; + + if (shapes.size() == 0) { + id = 1; + } else { + id = shapes.back()->key() + 1; + } + + sd.owner = p_owner; + + shapes[id] = sd; + + return id; +} + +void CollisionObject2D::remove_shape_owner(uint32_t owner) { + + ERR_FAIL_COND(!shapes.has(owner)); + + shape_owner_clear_shapes(owner); + + shapes.erase(owner); +} + +void CollisionObject2D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabled) { + ERR_FAIL_COND(!shapes.has(p_owner)); + + ShapeData &sd = shapes[p_owner]; + sd.disabled = p_disabled; + for (int i = 0; i < sd.shapes.size(); i++) { + if (area) { + Physics2DServer::get_singleton()->area_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); + } else { + Physics2DServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); + } + } +} + +bool CollisionObject2D::is_shape_owner_disabled(uint32_t p_owner) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), false); + + return shapes[p_owner].disabled; +} + +void CollisionObject2D::shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable) { if (area) - Physics2DServer::get_singleton()->area_clear_shapes(rid); - else - Physics2DServer::get_singleton()->body_clear_shapes(rid); - - for (int i = 0; i < shapes.size(); i++) { - - if (shapes[i].shape.is_null()) - continue; - if (area) - Physics2DServer::get_singleton()->area_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform); - else { - Physics2DServer::get_singleton()->body_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform); - if (shapes[i].trigger) - Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, i, shapes[i].trigger); + return; //not for areas + + ERR_FAIL_COND(!shapes.has(p_owner)); + + ShapeData &sd = shapes[p_owner]; + sd.one_way_collision = p_enable; + for (int i = 0; i < sd.shapes.size(); i++) { + Physics2DServer::get_singleton()->body_set_shape_as_one_way_collision(rid, sd.shapes[i].index, p_enable); + } +} + +bool CollisionObject2D::is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), false); + + return shapes[p_owner].one_way_collision; +} + +void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) { + + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + r_owners->push_back(E->key()); + } +} + +void CollisionObject2D::shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform) { + + 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); + } else { + Physics2DServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform); } } } +Transform2D CollisionObject2D::shape_owner_get_transform(uint32_t p_owner) const { -bool CollisionObject2D::_set(const StringName &p_name, const Variant &p_value) { - String name = p_name; + ERR_FAIL_COND_V(!shapes.has(p_owner), Transform2D()); - if (name.begins_with("shapes/")) { + return shapes[p_owner].xform; +} - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - if (what == "shape") { - if (idx >= shapes.size()) - add_shape(RefPtr(p_value)); - else - set_shape(idx, RefPtr(p_value)); - } else if (what == "transform") - set_shape_transform(idx, p_value); - else if (what == "trigger") - set_shape_as_trigger(idx, p_value); - } else - return false; - - return true; +Object *CollisionObject2D::shape_owner_get_owner(uint32_t p_owner) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), NULL); + + return shapes[p_owner].owner; +} + +void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape) { + + ERR_FAIL_COND(!shapes.has(p_owner)); + ERR_FAIL_COND(p_shape.is_null()); + + ShapeData &sd = shapes[p_owner]; + ShapeData::Shape s; + s.index = total_subshapes; + s.shape = p_shape; + if (area) { + Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform); + } else { + Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform); + } + sd.shapes.push_back(s); + + total_subshapes++; +} +int CollisionObject2D::shape_owner_get_shape_count(uint32_t p_owner) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), 0); + + return shapes[p_owner].shapes.size(); +} +Ref<Shape> CollisionObject2D::shape_owner_get_shape(uint32_t p_owner, int p_shape) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), Ref<Shape>()); + ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), Ref<Shape>()); + + return shapes[p_owner].shapes[p_shape].shape; +} +int CollisionObject2D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const { + + ERR_FAIL_COND_V(!shapes.has(p_owner), -1); + ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), -1); + + return shapes[p_owner].shapes[p_shape].index; } -bool CollisionObject2D::_get(const StringName &p_name, Variant &r_ret) const { +void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { + + ERR_FAIL_COND(!shapes.has(p_owner)); + ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); + + int index_to_remove = shapes[p_owner].shapes[p_shape].index; + if (area) { + Physics2DServer::get_singleton()->area_remove_shape(rid, index_to_remove); + } else { + Physics2DServer::get_singleton()->body_remove_shape(rid, index_to_remove); + } + + shapes[p_owner].shapes.remove(p_shape); + + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().shapes.size(); i++) { + if (E->get().shapes[i].index > index_to_remove) { + E->get().shapes[i].index -= 1; + } + } + } - String name = p_name; + total_subshapes--; +} - if (name.begins_with("shapes/")) { +void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) { - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - if (what == "shape") - r_ret = get_shape(idx); - else if (what == "transform") - r_ret = get_shape_transform(idx); - else if (what == "trigger") - r_ret = is_shape_set_as_trigger(idx); - } else - return false; + ERR_FAIL_COND(!shapes.has(p_owner)); - return true; + while (shape_owner_get_shape_count(p_owner) > 0) { + shape_owner_remove_shape(p_owner, 0); + } } -void CollisionObject2D::_get_property_list(List<PropertyInfo> *p_list) const { +uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const { - //p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_NO_INSTANCE_STATE) ); + ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0); - for (int i = 0; i < shapes.size(); i++) { - String path = "shapes/" + itos(i) + "/"; - p_list->push_back(PropertyInfo(Variant::OBJECT, path + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, path + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE)); - p_list->push_back(PropertyInfo(Variant::BOOL, path + "trigger", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE)); + for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().shapes.size(); i++) { + if (E->get().shapes[i].index == p_shape_index) { + return E->key(); + } + } } + + //in theory it should be unreachable + return 0; } void CollisionObject2D::set_pickable(bool p_enabled) { @@ -216,16 +319,6 @@ void CollisionObject2D::_update_pickable() { void CollisionObject2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_shape", "shape:Shape2D", "transform"), &CollisionObject2D::add_shape, DEFVAL(Transform2D())); - ClassDB::bind_method(D_METHOD("get_shape_count"), &CollisionObject2D::get_shape_count); - ClassDB::bind_method(D_METHOD("set_shape", "shape_idx", "shape:Shape"), &CollisionObject2D::set_shape); - ClassDB::bind_method(D_METHOD("set_shape_transform", "shape_idx", "transform"), &CollisionObject2D::set_shape_transform); - ClassDB::bind_method(D_METHOD("set_shape_as_trigger", "shape_idx", "enable"), &CollisionObject2D::set_shape_as_trigger); - ClassDB::bind_method(D_METHOD("get_shape:Shape2D", "shape_idx"), &CollisionObject2D::get_shape); - ClassDB::bind_method(D_METHOD("get_shape_transform", "shape_idx"), &CollisionObject2D::get_shape_transform); - ClassDB::bind_method(D_METHOD("is_shape_set_as_trigger", "shape_idx"), &CollisionObject2D::is_shape_set_as_trigger); - ClassDB::bind_method(D_METHOD("remove_shape", "shape_idx"), &CollisionObject2D::remove_shape); - ClassDB::bind_method(D_METHOD("clear_shapes"), &CollisionObject2D::clear_shapes); ClassDB::bind_method(D_METHOD("get_rid"), &CollisionObject2D::get_rid); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); @@ -242,100 +335,13 @@ void CollisionObject2D::_bind_methods() { ADD_GROUP("", ""); } -void CollisionObject2D::add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform) { - - ERR_FAIL_COND(p_shape.is_null()); - - ShapeData sdata; - sdata.shape = p_shape; - sdata.xform = p_transform; - sdata.trigger = false; - - if (area) - Physics2DServer::get_singleton()->area_add_shape(get_rid(), p_shape->get_rid(), p_transform); - else - Physics2DServer::get_singleton()->body_add_shape(get_rid(), p_shape->get_rid(), p_transform); - - shapes.push_back(sdata); -} -int CollisionObject2D::get_shape_count() const { - - return shapes.size(); -} -void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape) { - - ERR_FAIL_INDEX(p_shape_idx, shapes.size()); - ERR_FAIL_COND(p_shape.is_null()); - - shapes[p_shape_idx].shape = p_shape; - if (area) - Physics2DServer::get_singleton()->area_set_shape(get_rid(), p_shape_idx, p_shape->get_rid()); - else - Physics2DServer::get_singleton()->body_set_shape(get_rid(), p_shape_idx, p_shape->get_rid()); - - //_update_shapes(); -} - -void CollisionObject2D::set_shape_transform(int p_shape_idx, const Transform2D &p_transform) { - - ERR_FAIL_INDEX(p_shape_idx, shapes.size()); - shapes[p_shape_idx].xform = p_transform; - - if (area) - Physics2DServer::get_singleton()->area_set_shape_transform(get_rid(), p_shape_idx, p_transform); - else - Physics2DServer::get_singleton()->body_set_shape_transform(get_rid(), p_shape_idx, p_transform); - - //_update_shapes(); -} - -Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const { - - ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Ref<Shape2D>()); - return shapes[p_shape_idx].shape; -} -Transform2D CollisionObject2D::get_shape_transform(int p_shape_idx) const { - - ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Transform2D()); - return shapes[p_shape_idx].xform; -} -void CollisionObject2D::remove_shape(int p_shape_idx) { - - ERR_FAIL_INDEX(p_shape_idx, shapes.size()); - shapes.remove(p_shape_idx); - - _update_shapes(); -} - -void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) { - - ERR_FAIL_INDEX(p_shape_idx, shapes.size()); - shapes[p_shape_idx].trigger = p_trigger; - if (!area && rid.is_valid()) { - - Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, p_shape_idx, p_trigger); - } -} - -bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const { - - ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), false); - return shapes[p_shape_idx].trigger; -} - -void CollisionObject2D::clear_shapes() { - - shapes.clear(); - - _update_shapes(); -} - CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { rid = p_rid; area = p_area; pickable = true; set_notify_transform(true); + total_subshapes = 0; if (p_area) { @@ -348,6 +354,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { CollisionObject2D::CollisionObject2D() { //owner= + set_notify_transform(true); } diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 4d4611afd1..deffe8a002 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -35,37 +35,40 @@ class CollisionObject2D : public Node2D { - GDCLASS(CollisionObject2D, Node2D); + GDCLASS(CollisionObject2D, Node2D) bool area; RID rid; bool pickable; struct ShapeData { + + Object *owner; Transform2D xform; - Ref<Shape2D> shape; - bool trigger; + struct Shape { + Ref<Shape2D> shape; + int index; + }; + + Vector<Shape> shapes; + bool disabled; + bool one_way_collision; ShapeData() { - trigger = false; + disabled = false; + one_way_collision = false; + owner = NULL; } }; - Vector<ShapeData> shapes; - - void _update_shapes(); + int total_subshapes; - friend class CollisionShape2D; - friend class CollisionPolygon2D; - void _update_shapes_from_children(); + Map<uint32_t, ShapeData> shapes; protected: CollisionObject2D(RID p_rid, bool p_area); void _notification(int p_what); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; static void _bind_methods(); void _update_pickable(); @@ -75,16 +78,29 @@ protected: void _mouse_exit(); public: - void add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform = Transform2D()); - int get_shape_count() const; - void set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape); - void set_shape_transform(int p_shape_idx, const Transform2D &p_transform); - Ref<Shape2D> get_shape(int p_shape_idx) const; - Transform2D get_shape_transform(int p_shape_idx) const; - void set_shape_as_trigger(int p_shape_idx, bool p_trigger); - bool is_shape_set_as_trigger(int p_shape_idx) const; - void remove_shape(int p_shape_idx); - void clear_shapes(); + uint32_t create_shape_owner(Object *p_owner); + void remove_shape_owner(uint32_t owner); + void get_shape_owners(List<uint32_t> *r_owners); + + void shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform); + Transform2D shape_owner_get_transform(uint32_t p_owner) const; + Object *shape_owner_get_owner(uint32_t p_owner) const; + + void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled); + bool is_shape_owner_disabled(uint32_t p_owner) const; + + void shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable); + bool is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const; + + void shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape); + int shape_owner_get_shape_count(uint32_t p_owner) const; + Ref<Shape> shape_owner_get_shape(uint32_t p_owner, int p_shape) const; + int shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const; + + void shape_owner_remove_shape(uint32_t p_owner, int p_shape); + void shape_owner_clear_shapes(uint32_t p_owner); + + uint32_t shape_find_owner(int p_shape_index) const; void set_pickable(bool p_enabled); bool is_pickable() const; diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 6b603c6473..bd669eb4c8 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -35,13 +35,9 @@ #include "thirdparty/misc/triangulator.h" -void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) { +void CollisionPolygon2D::_build_polygon() { - if (unparenting || !can_update_body) - return; - - CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>(); - ERR_FAIL_COND(!co); + parent->shape_owner_clear_shapes(owner_id); if (polygon.size() == 0) return; @@ -53,18 +49,10 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) { //here comes the sun, lalalala //decompose concave into multiple convex polygons and add them Vector<Vector<Vector2> > decomp = _decompose_in_convex(); - shape_from = co->get_shape_count(); for (int i = 0; i < decomp.size(); i++) { Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D); convex->set_points(decomp[i]); - co->add_shape(convex, get_transform()); - if (trigger) - co->set_shape_as_trigger(co->get_shape_count() - 1, true); - } - shape_to = co->get_shape_count() - 1; - if (shape_to < shape_from) { - shape_from = -1; - shape_to = -1; + parent->shape_owner_add_shape(owner_id, convex); } } else { @@ -83,28 +71,8 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) { w = PoolVector<Vector2>::Write(); concave->set_segments(segments); - co->add_shape(concave, get_transform()); - if (trigger) - co->set_shape_as_trigger(co->get_shape_count() - 1, true); - - shape_from = co->get_shape_count() - 1; - shape_to = co->get_shape_count() - 1; + parent->shape_owner_add_shape(owner_id, concave); } - - //co->add_shape(shape,get_transform()); -} - -void CollisionPolygon2D::_update_parent() { - - if (!can_update_body) - return; - Node *parent = get_parent(); - if (!parent) - return; - CollisionObject2D *co = parent->cast_to<CollisionObject2D>(); - if (!co) - return; - co->_update_shapes_from_children(); } Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() { @@ -155,33 +123,38 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() { void CollisionPolygon2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - unparenting = false; - can_update_body = get_tree()->is_editor_hint(); - if (!get_tree()->is_editor_hint()) { + case NOTIFICATION_PARENTED: { + + parent = get_parent()->cast_to<CollisionObject2D>(); + if (parent) { + owner_id = parent->create_shape_owner(this); + _build_polygon(); + parent->shape_owner_set_transform(owner_id, get_transform()); + parent->shape_owner_set_disabled(owner_id, disabled); + parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); + } + + /*if (get_tree()->is_editor_hint()) { //display above all else set_z_as_relative(false); set_z(VS::CANVAS_ITEM_Z_MAX - 1); - } + }*/ } break; - case NOTIFICATION_EXIT_TREE: { - can_update_body = false; - } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (!is_inside_tree()) - break; - if (can_update_body) { - _update_parent(); - } else if (shape_from >= 0 && shape_to >= 0) { - CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>(); - for (int i = shape_from; i <= shape_to; i++) { - co->set_shape_transform(i, get_transform()); - } + if (parent) { + parent->shape_owner_set_transform(owner_id, get_transform()); } } break; + case NOTIFICATION_UNPARENTED: { + if (parent) { + parent->remove_shape_owner(owner_id); + } + owner_id = 0; + parent = NULL; + } break; case NOTIFICATION_DRAW: { @@ -210,10 +183,22 @@ void CollisionPolygon2D::_notification(int p_what) { draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color()); #endif - } break; - case NOTIFICATION_UNPARENTED: { - unparenting = true; - _update_parent(); + if (one_way_collision) { + Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4); + dcol.a = 1.0; + Vector2 line_to(0, 20); + draw_line(Vector2(), line_to, dcol, 3); + 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))); + Vector<Color> cols; + for (int i = 0; i < 3; i++) + cols.push_back(dcol); + + draw_primitive(pts, cols, Vector<Vector2>()); //small arrow + } } break; } } @@ -222,7 +207,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) { polygon = p_polygon; - if (can_update_body) { + { for (int i = 0; i < polygon.size(); i++) { if (i == 0) aabb = Rect2(polygon[i], Size2()); @@ -236,7 +221,10 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) { aabb.position -= aabb.size * 0.3; aabb.size += aabb.size * 0.6; } - _update_parent(); + } + + if (parent) { + _build_polygon(); } update(); update_configuration_warning(); @@ -251,7 +239,9 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) { ERR_FAIL_INDEX(p_mode, 2); build_mode = p_mode; - _update_parent(); + if (parent) { + _build_polygon(); + } } CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const { @@ -264,79 +254,69 @@ Rect2 CollisionPolygon2D::get_item_rect() const { return aabb; } -void CollisionPolygon2D::set_trigger(bool p_trigger) { +String CollisionPolygon2D::get_configuration_warning() const { - trigger = p_trigger; - _update_parent(); - if (!can_update_body && is_inside_tree() && shape_from >= 0 && shape_to >= 0) { - CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>(); - for (int i = shape_from; i <= shape_to; i++) { - co->set_shape_as_trigger(i, p_trigger); - } + if (!get_parent()->cast_to<CollisionObject2D>()) { + return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); } -} -bool CollisionPolygon2D::is_trigger() const { + if (polygon.empty()) { + return TTR("An empty CollisionPolygon2D has no effect on collision."); + } - return trigger; + return String(); } -void CollisionPolygon2D::_set_shape_range(const Vector2 &p_range) { - - shape_from = p_range.x; - shape_to = p_range.y; +void CollisionPolygon2D::set_disabled(bool p_disabled) { + disabled = p_disabled; + update(); + if (parent) { + parent->shape_owner_set_disabled(owner_id, p_disabled); + } } -Vector2 CollisionPolygon2D::_get_shape_range() const { - - return Vector2(shape_from, shape_to); +bool CollisionPolygon2D::is_disabled() const { + return disabled; } -String CollisionPolygon2D::get_configuration_warning() const { - - if (!get_parent()->cast_to<CollisionObject2D>()) { - return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); +void CollisionPolygon2D::set_one_way_collision(bool p_enable) { + one_way_collision = p_enable; + update(); + if (parent) { + parent->shape_owner_set_one_way_collision(owner_id, p_enable); } +} - if (polygon.empty()) { - return TTR("An empty CollisionPolygon2D has no effect on collision."); - } +bool CollisionPolygon2D::is_one_way_collision_enabled() const { - return String(); + return one_way_collision; } void CollisionPolygon2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionPolygon2D::_add_to_collision_object); ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon); ClassDB::bind_method(D_METHOD("get_polygon"), &CollisionPolygon2D::get_polygon); ClassDB::bind_method(D_METHOD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode); ClassDB::bind_method(D_METHOD("get_build_mode"), &CollisionPolygon2D::get_build_mode); - - ClassDB::bind_method(D_METHOD("set_trigger", "trigger"), &CollisionPolygon2D::set_trigger); - ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionPolygon2D::is_trigger); - - ClassDB::bind_method(D_METHOD("_set_shape_range", "shape_range"), &CollisionPolygon2D::_set_shape_range); - ClassDB::bind_method(D_METHOD("_get_shape_range"), &CollisionPolygon2D::_get_shape_range); - - ClassDB::bind_method(D_METHOD("get_collision_object_first_shape"), &CollisionPolygon2D::get_collision_object_first_shape); - ClassDB::bind_method(D_METHOD("get_collision_object_last_shape"), &CollisionPolygon2D::get_collision_object_last_shape); + ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon2D::set_disabled); + ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon2D::is_disabled); + ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionPolygon2D::set_one_way_collision); + ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionPolygon2D::is_one_way_collision_enabled); ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shape_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_shape_range", "_get_shape_range"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled"); } CollisionPolygon2D::CollisionPolygon2D() { aabb = Rect2(-10, -10, 20, 20); build_mode = BUILD_SOLIDS; - trigger = false; - unparenting = false; - shape_from = -1; - shape_to = -1; - can_update_body = false; set_notify_local_transform(true); + parent = NULL; + owner_id = 0; + disabled = false; + one_way_collision = false; } diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index b1a4a4822d..f0666ba9de 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -33,6 +33,8 @@ #include "scene/2d/node_2d.h" #include "scene/resources/shape_2d.h" +class CollisionObject2D; + class CollisionPolygon2D : public Node2D { GDCLASS(CollisionPolygon2D, Node2D); @@ -47,29 +49,20 @@ protected: Rect2 aabb; BuildMode build_mode; Vector<Point2> polygon; - bool trigger; - bool unparenting; - - void _add_to_collision_object(Object *p_obj); - void _update_parent(); - - bool can_update_body; - int shape_from; - int shape_to; - - void _set_shape_range(const Vector2 &p_range); - Vector2 _get_shape_range() const; + uint32_t owner_id; + CollisionObject2D *parent; + bool disabled; + bool one_way_collision; Vector<Vector<Vector2> > _decompose_in_convex(); + void _build_polygon(); + protected: void _notification(int p_what); static void _bind_methods(); public: - void set_trigger(bool p_trigger); - bool is_trigger() const; - void set_build_mode(BuildMode p_mode); BuildMode get_build_mode() const; @@ -78,11 +71,14 @@ public: virtual Rect2 get_item_rect() const; - int get_collision_object_first_shape() const { return shape_from; } - int get_collision_object_last_shape() const { return shape_to; } - virtual String get_configuration_warning() const; + void set_disabled(bool p_disabled); + bool is_disabled() const; + + void set_one_way_collision(bool p_enable); + bool is_one_way_collision_enabled() const; + CollisionPolygon2D(); }; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 1687a898db..ff4aa245ec 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -37,68 +37,48 @@ #include "scene/resources/segment_shape_2d.h" #include "scene/resources/shape_line_2d.h" -void CollisionShape2D::_add_to_collision_object(Object *p_obj) { - - if (unparenting) - return; - - CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>(); - ERR_FAIL_COND(!co); - update_shape_index = co->get_shape_count(); - co->add_shape(shape, get_transform()); - if (trigger) - co->set_shape_as_trigger(co->get_shape_count() - 1, true); -} - void CollisionShape2D::_shape_changed() { update(); - _update_parent(); -} - -void CollisionShape2D::_update_parent() { - - Node *parent = get_parent(); - if (!parent) - return; - CollisionObject2D *co = parent->cast_to<CollisionObject2D>(); - if (!co) - return; - co->_update_shapes_from_children(); } void CollisionShape2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - unparenting = false; - can_update_body = get_tree()->is_editor_hint(); - if (!get_tree()->is_editor_hint()) { + case NOTIFICATION_PARENTED: { + + parent = get_parent()->cast_to<CollisionObject2D>(); + if (parent) { + owner_id = parent->create_shape_owner(this); + if (shape.is_valid()) { + parent->shape_owner_add_shape(owner_id, shape); + } + parent->shape_owner_set_transform(owner_id, get_transform()); + parent->shape_owner_set_disabled(owner_id, disabled); + parent->shape_owner_set_one_way_collision(owner_id, one_way_collision); + } + + /*if (get_tree()->is_editor_hint()) { //display above all else set_z_as_relative(false); set_z(VS::CANVAS_ITEM_Z_MAX - 1); - } + }*/ } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (!is_inside_tree()) - break; - if (can_update_body) { - _update_parent(); - } else if (update_shape_index >= 0) { - - CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>(); - if (co) { - co->set_shape_transform(update_shape_index, get_transform()); - } + if (parent) { + parent->shape_owner_set_transform(owner_id, get_transform()); } } break; - case NOTIFICATION_EXIT_TREE: { - can_update_body = false; - + case NOTIFICATION_UNPARENTED: { + if (parent) { + parent->remove_shape_owner(owner_id); + } + owner_id = 0; + parent = NULL; } break; /* case NOTIFICATION_TRANSFORM_CHANGED: { @@ -121,15 +101,33 @@ void CollisionShape2D::_notification(int p_what) { rect = Rect2(); Color draw_col = get_tree()->get_debug_collisions_color(); + if (disabled) { + float g = draw_col.gray(); + draw_col.r = g; + draw_col.g = g; + draw_col.b = g; + } shape->draw(get_canvas_item(), draw_col); rect = shape->get_rect(); rect = rect.grow(3); - } break; - case NOTIFICATION_UNPARENTED: { - unparenting = true; - _update_parent(); + if (one_way_collision) { + Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4); + dcol.a = 1.0; + Vector2 line_to(0, 20); + draw_line(Vector2(), line_to, dcol, 3); + 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))); + Vector<Color> cols; + for (int i = 0; i < 3; i++) + cols.push_back(dcol); + + draw_primitive(pts, cols, Vector<Vector2>()); //small arrow + } } break; } } @@ -140,14 +138,13 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) { shape->disconnect("changed", this, "_shape_changed"); shape = p_shape; update(); - if (is_inside_tree() && can_update_body) - _update_parent(); - if (is_inside_tree() && !can_update_body && update_shape_index >= 0) { - CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>(); - if (co) { - co->set_shape(update_shape_index, p_shape); + if (parent) { + parent->shape_owner_clear_shapes(owner_id); + if (shape.is_valid()) { + parent->shape_owner_add_shape(owner_id, shape); } } + if (shape.is_valid()) shape->connect("changed", this, "_shape_changed"); @@ -164,72 +161,65 @@ Rect2 CollisionShape2D::get_item_rect() const { return rect; } -void CollisionShape2D::set_trigger(bool p_trigger) { +String CollisionShape2D::get_configuration_warning() const { - trigger = p_trigger; - if (can_update_body) { - _update_parent(); - } else if (is_inside_tree() && update_shape_index >= 0) { - CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>(); - if (co) { - co->set_shape_as_trigger(update_shape_index, p_trigger); - } + if (!get_parent()->cast_to<CollisionObject2D>()) { + return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); } -} -bool CollisionShape2D::is_trigger() const { + if (!shape.is_valid()) { + return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"); + } - return trigger; + return String(); } -void CollisionShape2D::_set_update_shape_index(int p_index) { - - update_shape_index = p_index; +void CollisionShape2D::set_disabled(bool p_disabled) { + disabled = p_disabled; + update(); + if (parent) { + parent->shape_owner_set_disabled(owner_id, p_disabled); + } } -int CollisionShape2D::_get_update_shape_index() const { - - return update_shape_index; +bool CollisionShape2D::is_disabled() const { + return disabled; } -String CollisionShape2D::get_configuration_warning() const { - - if (!get_parent()->cast_to<CollisionObject2D>()) { - return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."); +void CollisionShape2D::set_one_way_collision(bool p_enable) { + one_way_collision = p_enable; + update(); + if (parent) { + parent->shape_owner_set_one_way_collision(owner_id, p_enable); } +} - if (!shape.is_valid()) { - return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!"); - } +bool CollisionShape2D::is_one_way_collision_enabled() const { - return String(); + return one_way_collision; } void CollisionShape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape2D::set_shape); ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape2D::get_shape); + ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionShape2D::set_disabled); + ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape2D::is_disabled); + ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionShape2D::set_one_way_collision); + ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled); ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape2D::_shape_changed); - ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionShape2D::_add_to_collision_object); - ClassDB::bind_method(D_METHOD("set_trigger", "enable"), &CollisionShape2D::set_trigger); - ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionShape2D::is_trigger); - - ClassDB::bind_method(D_METHOD("_set_update_shape_index", "index"), &CollisionShape2D::_set_update_shape_index); - ClassDB::bind_method(D_METHOD("_get_update_shape_index"), &CollisionShape2D::_get_update_shape_index); - - ClassDB::bind_method(D_METHOD("get_collision_object_shape_index"), &CollisionShape2D::get_collision_object_shape_index); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "_update_shape_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_update_shape_index", "_get_update_shape_index"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled"); } CollisionShape2D::CollisionShape2D() { rect = Rect2(-Point2(10, 10), Point2(20, 20)); set_notify_local_transform(true); - trigger = false; - unparenting = false; - can_update_body = false; - update_shape_index = -1; + owner_id = 0; + parent = NULL; + disabled = false; + one_way_collision = false; } diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 3e63981010..1f2b96b91f 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -33,35 +33,33 @@ #include "scene/2d/node_2d.h" #include "scene/resources/shape_2d.h" +class CollisionObject2D; + class CollisionShape2D : public Node2D { - GDCLASS(CollisionShape2D, Node2D); + GDCLASS(CollisionShape2D, Node2D) Ref<Shape2D> shape; Rect2 rect; - bool trigger; - bool unparenting; - bool can_update_body; + uint32_t owner_id; + CollisionObject2D *parent; void _shape_changed(); - int update_shape_index; - - void _set_update_shape_index(int p_index); - int _get_update_shape_index() const; + bool disabled; + bool one_way_collision; protected: - void _update_parent(); void _notification(int p_what); static void _bind_methods(); - void _add_to_collision_object(Object *p_obj); - public: void set_shape(const Ref<Shape2D> &p_shape); Ref<Shape2D> get_shape() const; virtual Rect2 get_item_rect() const; - void set_trigger(bool p_trigger); - bool is_trigger() const; - int get_collision_object_shape_index() const { return _get_update_shape_index(); } + void set_disabled(bool p_disabled); + bool is_disabled() const; + + void set_one_way_collision(bool p_enable); + bool is_one_way_collision_enabled() const; virtual String get_configuration_warning() const; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 1a57d24342..5438557d0b 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -310,12 +310,15 @@ void Line2D::_bind_methods() { 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::COLOR, "default_color"), "set_default_color", "get_default_color"); + ADD_GROUP("Fill", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile"), "set_texture_mode", "get_texture_mode"); + ADD_GROUP("Capping", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round"), "set_joint_mode", "get_joint_mode"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_begin_cap_mode", "get_begin_cap_mode"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round"), "set_end_cap_mode", "get_end_cap_mode"); + 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"); diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp index 21d64c5d64..aa9258c7b4 100644 --- a/scene/2d/particles_2d.cpp +++ b/scene/2d/particles_2d.cpp @@ -28,903 +28,246 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "particles_2d.h" -#include "scene/scene_string_names.h" - -void ParticleAttractor2D::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - _update_owner(); - } break; - case NOTIFICATION_DRAW: { - - if (!get_tree()->is_editor_hint()) - return; - - Vector2 pv; - float dr = MIN(disable_radius, radius); - for (int i = 0; i <= 32; i++) { - Vector2 v(Math::sin(i / 32.0 * Math_PI * 2), Math::cos(i / 32.0 * Math_PI * 2)); - if (i > 0) { - draw_line(pv * radius, v * radius, Color(0, 0, 0.5, 0.9)); - if (dr > 0) { - draw_line(pv * dr, v * dr, Color(0.5, 0, 0.0, 0.9)); - } - } - pv = v; - } +#include "scene/3d/particles.h" +#include "scene/scene_string_names.h" - } break; - case NOTIFICATION_EXIT_TREE: { - if (owner) { - _set_owner(NULL); - } +void Particles2D::set_emitting(bool p_emitting) { - } break; - } + emitting = p_emitting; + VS::get_singleton()->particles_set_emitting(particles, emitting); } -void ParticleAttractor2D::_owner_exited() { +void Particles2D::set_amount(int p_amount) { - ERR_FAIL_COND(!owner); - owner->attractors.erase(this); - owner = NULL; + ERR_FAIL_COND(p_amount < 1); + amount = p_amount; + VS::get_singleton()->particles_set_amount(particles, amount); } +void Particles2D::set_lifetime(float p_lifetime) { -void ParticleAttractor2D::_update_owner() { - - if (!is_inside_tree() || !has_node(path)) { - _set_owner(NULL); - return; - } - - Node *n = get_node(path); - ERR_FAIL_COND(!n); - Particles2D *pn = n->cast_to<Particles2D>(); - if (!pn) { - _set_owner(NULL); - return; - } - - _set_owner(pn); + ERR_FAIL_COND(p_lifetime <= 0); + lifetime = p_lifetime; + VS::get_singleton()->particles_set_lifetime(particles, lifetime); } -void ParticleAttractor2D::_set_owner(Particles2D *p_owner) { - - if (owner == p_owner) - return; - - if (owner) { - owner->disconnect("tree_exited", this, "_owner_exited"); - owner->attractors.erase(this); - owner = NULL; - } - owner = p_owner; - - if (owner) { +void Particles2D::set_one_shot(bool p_enable) { - owner->connect("tree_exited", this, "_owner_exited", varray(), CONNECT_ONESHOT); - owner->attractors.insert(this); - } + one_shot = p_enable; + VS::get_singleton()->particles_set_one_shot(particles, one_shot); } +void Particles2D::set_pre_process_time(float p_time) { -void ParticleAttractor2D::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ParticleAttractor2D::set_enabled); - ClassDB::bind_method(D_METHOD("is_enabled"), &ParticleAttractor2D::is_enabled); - - ClassDB::bind_method(D_METHOD("set_radius", "radius"), &ParticleAttractor2D::set_radius); - ClassDB::bind_method(D_METHOD("get_radius"), &ParticleAttractor2D::get_radius); - - ClassDB::bind_method(D_METHOD("set_disable_radius", "radius"), &ParticleAttractor2D::set_disable_radius); - ClassDB::bind_method(D_METHOD("get_disable_radius"), &ParticleAttractor2D::get_disable_radius); - - ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &ParticleAttractor2D::set_gravity); - ClassDB::bind_method(D_METHOD("get_gravity"), &ParticleAttractor2D::get_gravity); - - ClassDB::bind_method(D_METHOD("set_absorption", "absorption"), &ParticleAttractor2D::set_absorption); - ClassDB::bind_method(D_METHOD("get_absorption"), &ParticleAttractor2D::get_absorption); - - ClassDB::bind_method(D_METHOD("set_particles_path", "path"), &ParticleAttractor2D::set_particles_path); - ClassDB::bind_method(D_METHOD("get_particles_path"), &ParticleAttractor2D::get_particles_path); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_radius", "get_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "disable_radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_disable_radius", "get_disable_radius"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-512,512,0.01"), "set_gravity", "get_gravity"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "absorption", PROPERTY_HINT_RANGE, "0,512,0.01"), "set_absorption", "get_absorption"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "particles_path", PROPERTY_HINT_RESOURCE_TYPE, "Particles2D"), "set_particles_path", "get_particles_path"); + pre_process_time = p_time; + VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time); } +void Particles2D::set_explosiveness_ratio(float p_ratio) { -void ParticleAttractor2D::set_enabled(bool p_enabled) { - - enabled = p_enabled; + explosiveness_ratio = p_ratio; + VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio); } +void Particles2D::set_randomness_ratio(float p_ratio) { -bool ParticleAttractor2D::is_enabled() const { - - return enabled; + randomness_ratio = p_ratio; + VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio); } +void Particles2D::set_visibility_rect(const Rect2 &p_aabb) { -void ParticleAttractor2D::set_radius(float p_radius) { - - radius = p_radius; - update(); -} + visibility_rect = p_aabb; + Rect3 aabb; + aabb.position.x = p_aabb.position.x; + aabb.position.y = p_aabb.position.y; + aabb.size.x = p_aabb.size.x; + aabb.size.y = p_aabb.size.y; -float ParticleAttractor2D::get_radius() const { + VS::get_singleton()->particles_set_custom_aabb(particles, aabb); - return radius; -} - -void ParticleAttractor2D::set_disable_radius(float p_disable_radius) { - - disable_radius = p_disable_radius; + _change_notify("visibility_rect"); update(); } -float ParticleAttractor2D::get_disable_radius() const { - - return disable_radius; -} - -void ParticleAttractor2D::set_gravity(float p_gravity) { - - gravity = p_gravity; -} -float ParticleAttractor2D::get_gravity() const { - - return gravity; -} - -void ParticleAttractor2D::set_absorption(float p_absorption) { +void Particles2D::set_use_local_coordinates(bool p_enable) { - absorption = p_absorption; -} -float ParticleAttractor2D::get_absorption() const { - - return absorption; -} - -void ParticleAttractor2D::set_particles_path(NodePath p_path) { - - path = p_path; - _update_owner(); - update_configuration_warning(); -} -NodePath ParticleAttractor2D::get_particles_path() const { - - return path; -} - -String ParticleAttractor2D::get_configuration_warning() const { - - if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) { - return TTR("Path property must point to a valid Particles2D node to work."); + local_coords = p_enable; + VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords); + set_notify_transform(!p_enable); + if (!p_enable && is_inside_tree()) { + _update_particle_emission_transform(); } - - return String(); } -ParticleAttractor2D::ParticleAttractor2D() { +void Particles2D::_update_particle_emission_transform() { - owner = NULL; - radius = 50; - disable_radius = 0; - gravity = 100; - absorption = 0; - path = String(".."); - enabled = true; -} - -/****************************************/ + Transform2D xf2d = get_global_transform(); + Transform xf; + xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0)); + xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0)); + xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0)); -_FORCE_INLINE_ static float _rand_from_seed(uint64_t *seed) { - - uint32_t r = Math::rand_from_seed(seed); - return 2.0f * (float)r / (float)Math::RANDOM_MAX - 1.0f; + VS::get_singleton()->particles_set_emission_transform(particles, xf); } -void Particles2D::_process_particles(float p_delta) { - - if (particles.size() == 0 || lifetime == 0) - return; - - p_delta *= time_scale; - - float frame_time = p_delta; - - if (emit_timeout > 0) { - time_to_live -= frame_time; - if (time_to_live < 0) { - - emitting = false; - _change_notify("config/emitting"); - }; - }; - - float next_time = time + frame_time; - - if (next_time > lifetime) - next_time = Math::fmod(next_time, lifetime); - - Particle *pdata = &particles[0]; - int particle_count = particles.size(); - Transform2D xform; - if (!local_space) - xform = get_global_transform(); - - active_count = 0; +void Particles2D::set_process_material(const Ref<Material> &p_material) { - PoolVector<Point2>::Read r; - int emission_point_count = 0; - if (emission_points.size()) { - - emission_point_count = emission_points.size(); - r = emission_points.read(); - } - - int attractor_count = 0; - AttractorCache *attractor_ptr = NULL; - - if (attractors.size()) { - if (attractors.size() != attractor_cache.size()) { - attractor_cache.resize(attractors.size()); - } - - int idx = 0; - Transform2D m; - if (local_space) { - m = get_global_transform().affine_inverse(); - } - for (Set<ParticleAttractor2D *>::Element *E = attractors.front(); E; E = E->next()) { - - attractor_cache[idx].pos = m.xform(E->get()->get_global_position()); - attractor_cache[idx].attractor = E->get(); - idx++; - } - - attractor_ptr = attractor_cache.ptr(); - attractor_count = attractor_cache.size(); + process_material = p_material; + Ref<ParticlesMaterial> pm = p_material; + if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) { + //likely a new material, modify it! + pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true); + pm->set_gravity(Vector3(0, 98, 0)); } + RID material_rid; + if (process_material.is_valid()) + material_rid = process_material->get_rid(); + VS::get_singleton()->particles_set_process_material(particles, material_rid); - for (int i = 0; i < particle_count; i++) { - - Particle &p = pdata[i]; - - float restart_time = (i * lifetime / particle_count) * explosiveness; - - bool restart = false; - - if (next_time < time) { - - if (restart_time > time || restart_time < next_time) - restart = true; - - } else if (restart_time > time && restart_time < next_time) { - restart = true; - } - - if (restart) { - - if (emitting) { - - p.pos = emissor_offset; - if (emission_point_count) { - - Vector2 ep = r[Math::rand() % emission_point_count]; - if (!local_space) { - p.pos = xform.xform(p.pos + ep * extents); - } else { - p.pos += ep * extents; - } - } else { - if (!local_space) { - p.pos = xform.xform(p.pos + Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y))); - } else { - p.pos += Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y)); - } - } - p.seed = Math::rand() % 12345678; - uint64_t rand_seed = p.seed * (i + 1); - - float angle = Math::deg2rad(param[PARAM_DIRECTION] + _rand_from_seed(&rand_seed) * param[PARAM_SPREAD]); - - p.velocity = Vector2(Math::sin(angle), Math::cos(angle)); - if (!local_space) { - - p.velocity = xform.basis_xform(p.velocity).normalized(); - } - - p.velocity *= param[PARAM_LINEAR_VELOCITY] + param[PARAM_LINEAR_VELOCITY] * _rand_from_seed(&rand_seed) * randomness[PARAM_LINEAR_VELOCITY]; - p.velocity += initial_velocity; - p.active = true; - p.rot = Math::deg2rad(param[PARAM_INITIAL_ANGLE] + param[PARAM_INITIAL_ANGLE] * randomness[PARAM_INITIAL_ANGLE] * _rand_from_seed(&rand_seed)); - active_count++; - - p.frame = Math::fmod(param[PARAM_ANIM_INITIAL_POS] + randomness[PARAM_ANIM_INITIAL_POS] * _rand_from_seed(&rand_seed), 1.0f); - - } else { - - p.active = false; - } - - } else { - - if (!p.active) - continue; - - uint64_t rand_seed = p.seed * (i + 1); - - Vector2 force; - - //apply gravity - float gravity_dir = Math::deg2rad(param[PARAM_GRAVITY_DIRECTION] + 180 * randomness[PARAM_GRAVITY_DIRECTION] * _rand_from_seed(&rand_seed)); - force += Vector2(Math::sin(gravity_dir), Math::cos(gravity_dir)) * (param[PARAM_GRAVITY_STRENGTH] + param[PARAM_GRAVITY_STRENGTH] * randomness[PARAM_GRAVITY_STRENGTH] * _rand_from_seed(&rand_seed)); - //apply radial - Vector2 rvec = (p.pos - emissor_offset).normalized(); - force += rvec * (param[PARAM_RADIAL_ACCEL] + param[PARAM_RADIAL_ACCEL] * randomness[PARAM_RADIAL_ACCEL] * _rand_from_seed(&rand_seed)); - //apply orbit - float orbitvel = (param[PARAM_ORBIT_VELOCITY] + param[PARAM_ORBIT_VELOCITY] * randomness[PARAM_ORBIT_VELOCITY] * _rand_from_seed(&rand_seed)); - if (orbitvel != 0) { - Vector2 rel = p.pos - xform.elements[2]; - Transform2D rot(orbitvel * frame_time, Vector2()); - p.pos = rot.xform(rel) + xform.elements[2]; - } - - Vector2 tvec = rvec.tangent(); - force += tvec * (param[PARAM_TANGENTIAL_ACCEL] + param[PARAM_TANGENTIAL_ACCEL] * randomness[PARAM_TANGENTIAL_ACCEL] * _rand_from_seed(&rand_seed)); - - for (int j = 0; j < attractor_count; j++) { - - Vector2 vec = (attractor_ptr[j].pos - p.pos); - float vl = vec.length(); - - if (!attractor_ptr[j].attractor->enabled || vl == 0 || vl > attractor_ptr[j].attractor->radius) - continue; - - force += vec * attractor_ptr[j].attractor->gravity; - float fvl = p.velocity.length(); - if (fvl && attractor_ptr[j].attractor->absorption) { - Vector2 target = vec.normalized(); - p.velocity = p.velocity.normalized().linear_interpolate(target, MIN(frame_time * attractor_ptr[j].attractor->absorption, 1)) * fvl; - } - - if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) { - p.active = false; - } - } - - p.velocity += force * frame_time; - - if (param[PARAM_DAMPING]) { - float dmp = param[PARAM_DAMPING] + param[PARAM_DAMPING] * randomness[PARAM_DAMPING] * _rand_from_seed(&rand_seed); - float v = p.velocity.length(); - v -= dmp * frame_time; - if (v <= 0) { - p.velocity = Vector2(); - } else { - p.velocity = p.velocity.normalized() * v; - } - } - - p.pos += p.velocity * frame_time; - p.rot += Math::lerp(param[PARAM_SPIN_VELOCITY], param[PARAM_SPIN_VELOCITY] * randomness[PARAM_SPIN_VELOCITY] * _rand_from_seed(&rand_seed), randomness[PARAM_SPIN_VELOCITY]) * frame_time; - float anim_spd = param[PARAM_ANIM_SPEED_SCALE] + param[PARAM_ANIM_SPEED_SCALE] * randomness[PARAM_ANIM_SPEED_SCALE] * _rand_from_seed(&rand_seed); - p.frame = Math::fposmod(p.frame + (frame_time / lifetime) * anim_spd, 1.0f); - - active_count++; - } - } - - time = Math::fmod(time + frame_time, lifetime); - if (!emitting && active_count == 0) { - emit_signal(SceneStringNames::get_singleton()->emission_finished); - set_process(false); - set_fixed_process(false); - } - - update(); + update_configuration_warning(); } -void Particles2D::_notification(int p_what) { - - switch (p_what) { - - case NOTIFICATION_PROCESS: { - - _process_particles(get_process_delta_time()); - } break; - - case NOTIFICATION_FIXED_PROCESS: { - - _process_particles(get_fixed_process_delta_time()); - } break; - - case NOTIFICATION_ENTER_TREE: { - - float ppt = preprocess; - while (ppt > 0) { - _process_particles(0.1); - ppt -= 0.1; - } - } break; - case NOTIFICATION_DRAW: { - - if (particles.size() == 0 || lifetime == 0) - return; - - RID ci = get_canvas_item(); - Size2 size(1, 1); - Point2 center; - int total_frames = 1; +void Particles2D::set_speed_scale(float p_scale) { - if (!texture.is_null()) { - size = texture->get_size(); - size.x /= h_frames; - size.y /= v_frames; - total_frames = h_frames * v_frames; - } - - float time_pos = (time / lifetime); - - Particle *pdata = &particles[0]; - int particle_count = particles.size(); - - RID texrid; - - if (texture.is_valid()) - texrid = texture->get_rid(); - - Transform2D invxform; - if (!local_space) - invxform = get_global_transform().affine_inverse(); - - int start_particle = (int)(time * (float)particle_count / lifetime); - - for (int id = 0; id < particle_count; ++id) { - int i = start_particle + id; - if (i >= particle_count) { - i -= particle_count; - } - - Particle &p = pdata[i]; - if (!p.active) - continue; - - float ptime = ((float)i / particle_count) * explosiveness; - - if (ptime < time_pos) - ptime = time_pos - ptime; - else - ptime = (1.0 - ptime) + time_pos; - - uint64_t rand_seed = p.seed * (i + 1); - - Color color; - - if (gradient.is_valid()) { - color = gradient->get_color_at_offset(ptime); - } else { - color = default_color; - } - - { - float huerand = _rand_from_seed(&rand_seed); - float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand; - - if (Math::abs(huerot) > CMP_EPSILON) { - - float h = color.get_h(); - float s = color.get_s(); - float v = color.get_v(); - float a = color.a; - //float preh=h; - h += huerot; - h = Math::abs(Math::fposmod(h, 1.0f)); - //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand)); - //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h)); - color.set_hsv(h, s, v); - color.a = a; - } - } - - float initial_size = param[PARAM_INITIAL_SIZE] + param[PARAM_INITIAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_INITIAL_SIZE]; - float final_size = param[PARAM_FINAL_SIZE] + param[PARAM_FINAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_FINAL_SIZE]; - - float size_mult = initial_size * (1.0 - ptime) + final_size * ptime; - - //Size2 rectsize=size * size_mult; - //rectsize=rectsize.floor(); - - //Rect2 r = Rect2(Vecto,rectsize); - - Transform2D xform; - - if (p.rot) { - - xform.set_rotation(p.rot); - xform.translate(-size * size_mult / 2.0); - xform.elements[2] += p.pos; - } else { - xform.elements[2] = -size * size_mult / 2.0; - xform.elements[2] += p.pos; - } - - if (!local_space) { - xform = invxform * xform; - } - - xform.scale_basis(Size2(size_mult, size_mult)); - - VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform); - - if (texrid.is_valid()) { - - Rect2 src_rect; - src_rect.size = size; - - if (total_frames > 1) { - int frame = Math::fast_ftoi(Math::floor(p.frame * total_frames)) % total_frames; - src_rect.position.x = size.x * (frame % h_frames); - src_rect.position.y = size.y * (frame / h_frames); - } - Rect2 dst_rect(Point2(), size); - if (flip_h) - dst_rect.size.x = -dst_rect.size.x; - if (flip_v) - dst_rect.size.y = -dst_rect.size.y; - - texture->draw_rect_region(ci, dst_rect, src_rect, color); - //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color); - } else { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(), size), color); - } - } - - } break; - } -} - -static const char *_particlesframe_property_names[Particles2D::PARAM_MAX] = { - "params/direction", - "params/spread", - "params/linear_velocity", - "params/spin_velocity", - "params/orbit_velocity", - "params/gravity_direction", - "params/gravity_strength", - "params/radial_accel", - "params/tangential_accel", - "params/damping", - "params/initial_angle", - "params/initial_size", - "params/final_size", - "params/hue_variation", - "params/anim_speed_scale", - "params/anim_initial_pos", -}; - -static const char *_particlesframe_property_rnames[Particles2D::PARAM_MAX] = { - "randomness/direction", - "randomness/spread", - "randomness/linear_velocity", - "randomness/spin_velocity", - "randomness/orbit_velocity", - "randomness/gravity_direction", - "randomness/gravity_strength", - "randomness/radial_accel", - "randomness/tangential_accel", - "randomness/damping", - "randomness/initial_angle", - "randomness/initial_size", - "randomness/final_size", - "randomness/hue_variation", - "randomness/anim_speed_scale", - "randomness/anim_initial_pos", -}; - -static const char *_particlesframe_property_ranges[Particles2D::PARAM_MAX] = { - "0,360,0.01", - "0,180,0.01", - "-1024,1024,0.01", - "-1024,1024,0.01", - "-1024,1024,0.01", - "0,360,0.01", - "0,1024,0.01", - "-128,128,0.01", - "-128,128,0.01", - "0,1024,0.001", - "0,360,0.01", - "0,1024,0.01", - "0,1024,0.01", - "0,1,0.01", - "0,128,0.01", - "0,1,0.01", -}; - -void Particles2D::set_emitting(bool p_emitting) { - - if (emitting == p_emitting) - return; - - if (p_emitting) { - - if (active_count == 0) - time = 0; - set_process(process_mode == PROCESS_IDLE); - set_fixed_process(process_mode == PROCESS_FIXED); - time_to_live = emit_timeout; - }; - emitting = p_emitting; - _change_notify("config/emitting"); + speed_scale = p_scale; + VS::get_singleton()->particles_set_speed_scale(particles, p_scale); } bool Particles2D::is_emitting() const { return emitting; } - -void Particles2D::set_process_mode(ProcessMode p_mode) { - - process_mode = p_mode; - const bool should_process = emitting || active_count != 0; - set_process(should_process && process_mode == PROCESS_IDLE); - set_fixed_process(should_process && process_mode == PROCESS_FIXED); -} - -Particles2D::ProcessMode Particles2D::get_process_mode() const { - - return process_mode; -} - -void Particles2D::set_amount(int p_amount) { - - ERR_FAIL_INDEX(p_amount, 1024 + 1); - - particles.resize(p_amount); -} int Particles2D::get_amount() const { - return particles.size(); -} - -void Particles2D::set_emit_timeout(float p_timeout) { - - emit_timeout = p_timeout; - time_to_live = p_timeout; -}; - -float Particles2D::get_emit_timeout() const { - - return emit_timeout; -}; - -void Particles2D::set_lifetime(float p_lifetime) { - - ERR_FAIL_INDEX(p_lifetime, 3600 + 1); - - lifetime = p_lifetime; + return amount; } float Particles2D::get_lifetime() const { return lifetime; } -void Particles2D::set_time_scale(float p_time_scale) { +bool Particles2D::get_one_shot() const { - time_scale = p_time_scale; + return one_shot; } -float Particles2D::get_time_scale() const { - - return time_scale; -} - -void Particles2D::set_pre_process_time(float p_pre_process_time) { - - preprocess = p_pre_process_time; -} - float Particles2D::get_pre_process_time() const { - return preprocess; -} - -void Particles2D::set_param(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - param[p_param] = p_value; + return pre_process_time; } -float Particles2D::get_param(Parameter p_param) const { +float Particles2D::get_explosiveness_ratio() const { - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return param[p_param]; + return explosiveness_ratio; } +float Particles2D::get_randomness_ratio() const { -void Particles2D::set_randomness(Parameter p_param, float p_value) { - - ERR_FAIL_INDEX(p_param, PARAM_MAX); - randomness[p_param] = p_value; + return randomness_ratio; } -float Particles2D::get_randomness(Parameter p_param) const { +Rect2 Particles2D::get_visibility_rect() const { - ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return randomness[p_param]; + return visibility_rect; } +bool Particles2D::get_use_local_coordinates() const { -void Particles2D::set_texture(const Ref<Texture> &p_texture) { - - texture = p_texture; -} - -Ref<Texture> Particles2D::get_texture() const { - - return texture; + return local_coords; } +Ref<Material> Particles2D::get_process_material() const { -void Particles2D::set_color(const Color &p_color) { - - default_color = p_color; + return process_material; } -Color Particles2D::get_color() const { +float Particles2D::get_speed_scale() const { - return default_color; + return speed_scale; } -void Particles2D::set_gradient(const Ref<Gradient> &p_gradient) { +void Particles2D::set_draw_order(DrawOrder p_order) { - gradient = p_gradient; + draw_order = p_order; + VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order)); } -Ref<Gradient> Particles2D::get_gradient() const { +Particles2D::DrawOrder Particles2D::get_draw_order() const { - return gradient; + return draw_order; } -void Particles2D::set_emissor_offset(const Point2 &p_offset) { - - emissor_offset = p_offset; +void Particles2D::set_fixed_fps(int p_count) { + fixed_fps = p_count; + VS::get_singleton()->particles_set_fixed_fps(particles, p_count); } -Point2 Particles2D::get_emissor_offset() const { - - return emissor_offset; +int Particles2D::get_fixed_fps() const { + return fixed_fps; } -void Particles2D::set_use_local_space(bool p_use) { - - local_space = p_use; +void Particles2D::set_fractional_delta(bool p_enable) { + fractional_delta = p_enable; + VS::get_singleton()->particles_set_fractional_delta(particles, p_enable); } -bool Particles2D::is_using_local_space() const { - - return local_space; +bool Particles2D::get_fractional_delta() const { + return fractional_delta; } -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phases(int p_phases) { - - //Create color ramp if we have 2 or more phases. - //Otherwise first phase phase will be assigned to default color. - if (p_phases > 1 && gradient.is_null()) { - gradient = Ref<Gradient>(memnew(Gradient())); - } - if (gradient.is_valid()) { - gradient->get_points().resize(p_phases); - } -} +String Particles2D::get_configuration_warning() const { -//Deprecated. -int Particles2D::get_color_phases() const { + String warnings; - if (gradient.is_valid()) { - return gradient->get_points_count(); + if (process_material.is_null()) { + if (warnings != String()) + warnings += "\n"; + warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); } - return 0; -} - -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phase_color(int p_phase, const Color &p_color) { - - ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES); - if (gradient.is_valid()) { - if (gradient->get_points_count() > p_phase) - gradient->set_color(p_phase, p_color); - } else { - if (p_phase == 0) - default_color = p_color; - } -} - -//Deprecated. -Color Particles2D::get_color_phase_color(int p_phase) const { - - ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, Color()); - if (gradient.is_valid()) { - return gradient->get_color(p_phase); - } - return Color(0, 0, 0, 1); -} - -//Deprecated. Converts color phases to color ramp -void Particles2D::set_color_phase_pos(int p_phase, float p_pos) { - ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES); - ERR_FAIL_COND(p_pos < 0.0 || p_pos > 1.0); - if (gradient.is_valid() && gradient->get_points_count() > p_phase) { - return gradient->set_offset(p_phase, p_pos); - } -} - -//Deprecated. -float Particles2D::get_color_phase_pos(int p_phase) const { - - ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, 0); - if (gradient.is_valid()) { - return gradient->get_offset(p_phase); - } - return 0; -} - -void Particles2D::set_emission_half_extents(const Vector2 &p_extents) { - extents = p_extents; + return warnings; } -Vector2 Particles2D::get_emission_half_extents() const { +Rect2 Particles2D::capture_rect() const { - return extents; + Rect3 aabb = VS::get_singleton()->particles_get_current_aabb(particles); + Rect2 r; + r.position.x = aabb.position.x; + r.position.y = aabb.position.y; + r.size.x = aabb.size.x; + r.size.y = aabb.size.y; + return r; } -void Particles2D::set_initial_velocity(const Vector2 &p_velocity) { - - initial_velocity = p_velocity; -} -Vector2 Particles2D::get_initial_velocity() const { - - return initial_velocity; +void Particles2D::set_texture(const Ref<Texture> &p_texture) { + texture = p_texture; + update(); } -void Particles2D::pre_process(float p_delta) { - - _process_particles(p_delta); +Ref<Texture> Particles2D::get_texture() const { + return texture; } -void Particles2D::set_explosiveness(float p_value) { +void Particles2D::set_normal_map(const Ref<Texture> &p_normal_map) { - explosiveness = p_value; + normal_map = p_normal_map; + update(); } -float Particles2D::get_explosiveness() const { - - return explosiveness; +Ref<Texture> Particles2D::get_normal_map() const { + return normal_map; } -void Particles2D::set_flip_h(bool p_flip) { - - flip_h = p_flip; +void Particles2D::_validate_property(PropertyInfo &property) const { } -bool Particles2D::is_flipped_h() const { +void Particles2D::set_v_frames(int p_count) { - return flip_h; + ERR_FAIL_COND(p_count < 1); + v_frames = p_count; + update(); } -void Particles2D::set_flip_v(bool p_flip) { - - flip_v = p_flip; -} -bool Particles2D::is_flipped_v() const { +int Particles2D::get_v_frames() const { - return flip_v; + return v_frames; } -void Particles2D::set_h_frames(int p_frames) { +void Particles2D::set_h_frames(int p_count) { - ERR_FAIL_COND(p_frames < 1); - h_frames = p_frames; + ERR_FAIL_COND(p_count < 1); + h_frames = p_count; + update(); } int Particles2D::get_h_frames() const { @@ -932,215 +275,133 @@ int Particles2D::get_h_frames() const { return h_frames; } -void Particles2D::set_v_frames(int p_frames) { - - ERR_FAIL_COND(p_frames < 1); - v_frames = p_frames; +void Particles2D::restart() { + VS::get_singleton()->particles_restart(particles); } -int Particles2D::get_v_frames() const { - return v_frames; -} +void Particles2D::_notification(int p_what) { -void Particles2D::set_emission_points(const PoolVector<Vector2> &p_points) { + if (p_what == NOTIFICATION_DRAW) { - emission_points = p_points; -} + RID texture_rid; + if (texture.is_valid()) + texture_rid = texture->get_rid(); + RID normal_rid; + if (normal_map.is_valid()) + normal_rid = texture->get_rid(); -PoolVector<Vector2> Particles2D::get_emission_points() const { + VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames); - return emission_points; -} +#ifdef TOOLS_ENABLED + if (get_tree()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) { -void Particles2D::reset() { + draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false); + } +#endif + } - for (int i = 0; i < particles.size(); i++) { - particles[i].active = false; + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + _update_particle_emission_transform(); } - time = 0; - active_count = 0; } void Particles2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_emitting", "active"), &Particles2D::set_emitting); - ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting); - - ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Particles2D::set_process_mode); - ClassDB::bind_method(D_METHOD("get_process_mode"), &Particles2D::get_process_mode); - + ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles2D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles2D::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount); + ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("set_one_shot", "secs"), &Particles2D::set_one_shot); + ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles2D::set_pre_process_time); + ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles2D::set_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles2D::set_randomness_ratio); + ClassDB::bind_method(D_METHOD("set_visibility_rect", "aabb"), &Particles2D::set_visibility_rect); + ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles2D::set_use_local_coordinates); + ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles2D::set_fixed_fps); + ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles2D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_process_material", "material:Material"), &Particles2D::set_process_material); + ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles2D::set_speed_scale); - ClassDB::bind_method(D_METHOD("set_lifetime", "lifetime"), &Particles2D::set_lifetime); + ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting); + ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount); ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles2D::get_lifetime); - - ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Particles2D::set_time_scale); - ClassDB::bind_method(D_METHOD("get_time_scale"), &Particles2D::get_time_scale); - - ClassDB::bind_method(D_METHOD("set_pre_process_time", "time"), &Particles2D::set_pre_process_time); + ClassDB::bind_method(D_METHOD("get_one_shot"), &Particles2D::get_one_shot); ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles2D::get_pre_process_time); - - ClassDB::bind_method(D_METHOD("set_emit_timeout", "value"), &Particles2D::set_emit_timeout); - ClassDB::bind_method(D_METHOD("get_emit_timeout"), &Particles2D::get_emit_timeout); - - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &Particles2D::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &Particles2D::get_param); - - ClassDB::bind_method(D_METHOD("set_randomness", "param", "value"), &Particles2D::set_randomness); - ClassDB::bind_method(D_METHOD("get_randomness", "param"), &Particles2D::get_randomness); - - ClassDB::bind_method(D_METHOD("set_texture:Texture", "texture"), &Particles2D::set_texture); + ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles2D::get_explosiveness_ratio); + ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles2D::get_randomness_ratio); + ClassDB::bind_method(D_METHOD("get_visibility_rect"), &Particles2D::get_visibility_rect); + ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles2D::get_use_local_coordinates); + ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles2D::get_fixed_fps); + ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles2D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_process_material:Material"), &Particles2D::get_process_material); + ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles2D::get_speed_scale); + + ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles2D::set_draw_order); + ClassDB::bind_method(D_METHOD("get_draw_order"), &Particles2D::get_draw_order); + + ClassDB::bind_method(D_METHOD("set_texture", "texture:Texture"), &Particles2D::set_texture); ClassDB::bind_method(D_METHOD("get_texture:Texture"), &Particles2D::get_texture); - ClassDB::bind_method(D_METHOD("set_color", "color"), &Particles2D::set_color); - ClassDB::bind_method(D_METHOD("get_color"), &Particles2D::get_color); - - ClassDB::bind_method(D_METHOD("set_gradient:Gradient", "gradient"), &Particles2D::set_gradient); - ClassDB::bind_method(D_METHOD("get_gradient:Gradient"), &Particles2D::get_gradient); + ClassDB::bind_method(D_METHOD("set_normal_map", "texture:Texture"), &Particles2D::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map:Texture"), &Particles2D::get_normal_map); - ClassDB::bind_method(D_METHOD("set_emissor_offset", "offset"), &Particles2D::set_emissor_offset); - ClassDB::bind_method(D_METHOD("get_emissor_offset"), &Particles2D::get_emissor_offset); - - ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &Particles2D::set_flip_h); - ClassDB::bind_method(D_METHOD("is_flipped_h"), &Particles2D::is_flipped_h); - - ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &Particles2D::set_flip_v); - ClassDB::bind_method(D_METHOD("is_flipped_v"), &Particles2D::is_flipped_v); - - ClassDB::bind_method(D_METHOD("set_h_frames", "enable"), &Particles2D::set_h_frames); - ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames); + ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect); - ClassDB::bind_method(D_METHOD("set_v_frames", "enable"), &Particles2D::set_v_frames); + ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames); ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames); - ClassDB::bind_method(D_METHOD("set_emission_half_extents", "extents"), &Particles2D::set_emission_half_extents); - ClassDB::bind_method(D_METHOD("get_emission_half_extents"), &Particles2D::get_emission_half_extents); - - ClassDB::bind_method(D_METHOD("set_color_phases", "phases"), &Particles2D::set_color_phases); - ClassDB::bind_method(D_METHOD("get_color_phases"), &Particles2D::get_color_phases); - - ClassDB::bind_method(D_METHOD("set_color_phase_color", "phase", "color"), &Particles2D::set_color_phase_color); - ClassDB::bind_method(D_METHOD("get_color_phase_color", "phase"), &Particles2D::get_color_phase_color); - - ClassDB::bind_method(D_METHOD("set_color_phase_pos", "phase", "pos"), &Particles2D::set_color_phase_pos); - ClassDB::bind_method(D_METHOD("get_color_phase_pos", "phase"), &Particles2D::get_color_phase_pos); - - ClassDB::bind_method(D_METHOD("pre_process", "time"), &Particles2D::pre_process); - ClassDB::bind_method(D_METHOD("reset"), &Particles2D::reset); - - ClassDB::bind_method(D_METHOD("set_use_local_space", "enable"), &Particles2D::set_use_local_space); - ClassDB::bind_method(D_METHOD("is_using_local_space"), &Particles2D::is_using_local_space); - - ClassDB::bind_method(D_METHOD("set_initial_velocity", "velocity"), &Particles2D::set_initial_velocity); - ClassDB::bind_method(D_METHOD("get_initial_velocity"), &Particles2D::get_initial_velocity); - - ClassDB::bind_method(D_METHOD("set_explosiveness", "amount"), &Particles2D::set_explosiveness); - ClassDB::bind_method(D_METHOD("get_explosiveness"), &Particles2D::get_explosiveness); - - ClassDB::bind_method(D_METHOD("set_emission_points", "points"), &Particles2D::set_emission_points); - ClassDB::bind_method(D_METHOD("get_emission_points"), &Particles2D::get_emission_points); - - ADD_SIGNAL(MethodInfo("emission_finished")); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "config/amount", PROPERTY_HINT_EXP_RANGE, "1,1024"), "set_amount", "get_amount"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "config/lifetime", PROPERTY_HINT_EXP_RANGE, "0.1,3600,0.1"), "set_lifetime", "get_lifetime"); - ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/time_scale", PROPERTY_HINT_EXP_RANGE, "0.01,128,0.01"), "set_time_scale", "get_time_scale"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/preprocess", PROPERTY_HINT_EXP_RANGE, "0,3600,0.1"), "set_pre_process_time", "get_pre_process_time"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/emit_timeout", PROPERTY_HINT_RANGE, "0,3600,0.1"), "set_emit_timeout", "get_emit_timeout"); - ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "config/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), "set_process_mode", "get_process_mode"); - ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/offset"), "set_emissor_offset", "get_emissor_offset"); - ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/half_extents"), "set_emission_half_extents", "get_emission_half_extents"); - ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/local_space"), "set_use_local_space", "is_using_local_space"); - ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness", "get_explosiveness"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_h"), "set_flip_h", "is_flipped_h"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_v"), "set_flip_v", "is_flipped_v"); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/h_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_h_frames", "get_h_frames"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/v_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_v_frames", "get_v_frames"); - - for (int i = 0; i < PARAM_MAX; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::REAL, _particlesframe_property_names[i], PROPERTY_HINT_RANGE, _particlesframe_property_ranges[i]), "set_param", "get_param", i); - } - - for (int i = 0; i < PARAM_MAX; i++) { - ADD_PROPERTYINZ(PropertyInfo(Variant::REAL, _particlesframe_property_rnames[i], PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_randomness", "get_randomness", i); - } - - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "color_phases/count", PROPERTY_HINT_RANGE, "0,4,1", 0), "set_color_phases", "get_color_phases"); - - //Backward compatibility. They will be converted to color ramp - for (int i = 0; i < MAX_COLOR_PHASES; i++) { - String phase = "phase_" + itos(i) + "/"; - ADD_PROPERTYI(PropertyInfo(Variant::REAL, phase + "pos", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_color_phase_pos", "get_color_phase_pos", i); - ADD_PROPERTYI(PropertyInfo(Variant::COLOR, phase + "color", PROPERTY_HINT_NONE, "", 0), "set_color_phase_color", "get_color_phase_color", i); - } + ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames); + ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames); - ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "color/color"), "set_color", "get_color"); - ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "color/color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); - - ADD_PROPERTYNZ(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_emission_points", "get_emission_points"); - - BIND_CONSTANT(PARAM_DIRECTION); - BIND_CONSTANT(PARAM_SPREAD); - BIND_CONSTANT(PARAM_LINEAR_VELOCITY); - BIND_CONSTANT(PARAM_SPIN_VELOCITY); - BIND_CONSTANT(PARAM_ORBIT_VELOCITY); - BIND_CONSTANT(PARAM_GRAVITY_DIRECTION); - BIND_CONSTANT(PARAM_GRAVITY_STRENGTH); - BIND_CONSTANT(PARAM_RADIAL_ACCEL); - BIND_CONSTANT(PARAM_TANGENTIAL_ACCEL); - BIND_CONSTANT(PARAM_DAMPING); - BIND_CONSTANT(PARAM_INITIAL_ANGLE); - BIND_CONSTANT(PARAM_INITIAL_SIZE); - BIND_CONSTANT(PARAM_FINAL_SIZE); - BIND_CONSTANT(PARAM_HUE_VARIATION); - BIND_CONSTANT(PARAM_ANIM_SPEED_SCALE); - BIND_CONSTANT(PARAM_ANIM_INITIAL_POS); - BIND_CONSTANT(PARAM_MAX); - - BIND_CONSTANT(MAX_COLOR_PHASES); + ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount"); + ADD_GROUP("Time", ""); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "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_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.01,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::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", ""); + ADD_PROPERTY(PropertyInfo(Variant::RECT3, "visibility_rect"), "set_visibility_rect", "get_visibility_rect"); + 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_GROUP("Process Material", "process_"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); + ADD_GROUP("Textures", ""); + 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"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames"); + + BIND_CONSTANT(DRAW_ORDER_INDEX); + BIND_CONSTANT(DRAW_ORDER_LIFETIME); } Particles2D::Particles2D() { - for (int i = 0; i < PARAM_MAX; i++) { + particles = VS::get_singleton()->particles_create(); - param[i] = 0; - randomness[i] = 0; - } - - set_param(PARAM_SPREAD, 10); - set_param(PARAM_LINEAR_VELOCITY, 20); - set_param(PARAM_GRAVITY_STRENGTH, 9.8); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - set_param(PARAM_INITIAL_ANGLE, 0.0); - set_param(PARAM_INITIAL_SIZE, 1.0); - set_param(PARAM_FINAL_SIZE, 1.0); - set_param(PARAM_ANIM_SPEED_SCALE, 1.0); - - set_color(Color(1, 1, 1, 1)); - - time = 0; - lifetime = 2; - emitting = false; - particles.resize(32); - active_count = -1; set_emitting(true); - process_mode = PROCESS_IDLE; - local_space = true; - preprocess = 0; - time_scale = 1.0; - - flip_h = false; - flip_v = false; - - v_frames = 1; + set_one_shot(false); + set_amount(8); + set_lifetime(1); + set_fixed_fps(0); + set_fractional_delta(true); + set_pre_process_time(0); + set_explosiveness_ratio(0); + set_randomness_ratio(0); + set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200))); + set_use_local_coordinates(true); + set_speed_scale(1); h_frames = 1; + v_frames = 1; +} + +Particles2D::~Particles2D() { - emit_timeout = 0; - time_to_live = 0; - explosiveness = 1.0; + VS::get_singleton()->free(particles); } diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h index 856beaa836..23278ce746 100644 --- a/scene/2d/particles_2d.h +++ b/scene/2d/particles_2d.h @@ -34,235 +34,102 @@ #include "scene/resources/color_ramp.h" #include "scene/resources/texture.h" -class Particles2D; -class ParticleAttractor2D : public Node2D { - - GDCLASS(ParticleAttractor2D, Node2D); - - friend class Particles2D; - bool enabled; - float radius; - float disable_radius; - float gravity; - float absorption; - NodePath path; - - Particles2D *owner; - - void _update_owner(); - void _owner_exited(); - void _set_owner(Particles2D *p_owner); - - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_enabled(bool p_enabled); - bool is_enabled() const; - - void set_radius(float p_radius); - float get_radius() const; - - void set_disable_radius(float p_disable_radius); - float get_disable_radius() const; - - void set_gravity(float p_gravity); - float get_gravity() const; - - void set_absorption(float p_absorption); - float get_absorption() const; - - void set_particles_path(NodePath p_path); - NodePath get_particles_path() const; - - virtual String get_configuration_warning() const; - - ParticleAttractor2D(); -}; - class Particles2D : public Node2D { - - GDCLASS(Particles2D, Node2D); +private: + GDCLASS(Particles2D, Node2D) public: - enum Parameter { - PARAM_DIRECTION, - PARAM_SPREAD, - PARAM_LINEAR_VELOCITY, - PARAM_SPIN_VELOCITY, - PARAM_ORBIT_VELOCITY, - PARAM_GRAVITY_DIRECTION, - PARAM_GRAVITY_STRENGTH, - PARAM_RADIAL_ACCEL, - PARAM_TANGENTIAL_ACCEL, - PARAM_DAMPING, - PARAM_INITIAL_ANGLE, - PARAM_INITIAL_SIZE, - PARAM_FINAL_SIZE, - PARAM_HUE_VARIATION, - PARAM_ANIM_SPEED_SCALE, - PARAM_ANIM_INITIAL_POS, - PARAM_MAX - }; - - enum { - MAX_COLOR_PHASES = 4 - }; - - enum ProcessMode { - PROCESS_FIXED, - PROCESS_IDLE, + enum DrawOrder { + DRAW_ORDER_INDEX, + DRAW_ORDER_LIFETIME, }; private: - float param[PARAM_MAX]; - float randomness[PARAM_MAX]; + RID particles; - struct Particle { - bool active; - Point2 pos; - Vector2 velocity; - float rot; - float frame; - uint64_t seed; - Particle() { - active = false; - seed = 123465789; - rot = 0; - frame = 0; - } - }; - - Vector<Particle> particles; - - struct AttractorCache { - - Vector2 pos; - ParticleAttractor2D *attractor; - }; - - Vector<AttractorCache> attractor_cache; - - float explosiveness; - float preprocess; - float lifetime; bool emitting; - bool local_space; - float emit_timeout; - float time_to_live; - float time_scale; - bool flip_h; - bool flip_v; - int h_frames; + bool one_shot; + int amount; + float lifetime; + float pre_process_time; + float explosiveness_ratio; + float randomness_ratio; + float speed_scale; + Rect2 visibility_rect; + bool local_coords; + int fixed_fps; + bool fractional_delta; int v_frames; - Point2 emissor_offset; - Vector2 initial_velocity; - Vector2 extents; - PoolVector<Vector2> emission_points; + int h_frames; - ProcessMode process_mode; + Ref<Material> process_material; - float time; - int active_count; + DrawOrder draw_order; Ref<Texture> texture; + Ref<Texture> normal_map; - //If no color ramp is set then default color is used. Created as simple alternative to color_ramp. - Color default_color; - Ref<Gradient> gradient; - - void _process_particles(float p_delta); - friend class ParticleAttractor2D; - - Set<ParticleAttractor2D *> attractors; + void _update_particle_emission_transform(); protected: - void _notification(int p_what); static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); public: void set_emitting(bool p_emitting); - bool is_emitting() const; - - void set_process_mode(ProcessMode p_mode); - ProcessMode get_process_mode() const; - void set_amount(int p_amount); - int get_amount() const; - void set_lifetime(float p_lifetime); - float get_lifetime() const; - - void set_time_scale(float p_time_scale); - float get_time_scale() const; + void set_one_shot(bool p_enabled); + void set_pre_process_time(float p_time); + void set_explosiveness_ratio(float p_ratio); + void set_randomness_ratio(float p_ratio); + void set_visibility_rect(const Rect2 &p_aabb); + void set_use_local_coordinates(bool p_enable); + void set_process_material(const Ref<Material> &p_material); + void set_speed_scale(float p_scale); - void set_pre_process_time(float p_pre_process_time); + bool is_emitting() const; + int get_amount() const; + float get_lifetime() const; + bool get_one_shot() const; float get_pre_process_time() const; + float get_explosiveness_ratio() const; + float get_randomness_ratio() const; + Rect2 get_visibility_rect() const; + bool get_use_local_coordinates() const; + Ref<Material> get_process_material() const; + float get_speed_scale() const; - void set_emit_timeout(float p_timeout); - float get_emit_timeout() const; - - void set_emission_half_extents(const Vector2 &p_extents); - Vector2 get_emission_half_extents() const; + void set_fixed_fps(int p_count); + int get_fixed_fps() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_fractional_delta(bool p_enable); + bool get_fractional_delta() const; - void set_randomness(Parameter p_randomness, float p_value); - float get_randomness(Parameter p_randomness) const; - - void set_explosiveness(float p_value); - float get_explosiveness() const; - - void set_flip_h(bool p_flip); - bool is_flipped_h() const; - - void set_flip_v(bool p_flip); - bool is_flipped_v() const; - - void set_h_frames(int p_frames); - int get_h_frames() const; - - void set_v_frames(int p_frames); - int get_v_frames() const; - - void set_color_phases(int p_phases); - int get_color_phases() const; - - void set_color_phase_color(int p_phase, const Color &p_color); - Color get_color_phase_color(int p_phase) const; - - void set_color_phase_pos(int p_phase, float p_pos); - float get_color_phase_pos(int p_phase) const; + void set_draw_order(DrawOrder p_order); + DrawOrder get_draw_order() const; void set_texture(const Ref<Texture> &p_texture); Ref<Texture> get_texture() const; - void set_color(const Color &p_color); - Color get_color() const; - - void set_gradient(const Ref<Gradient> &p_texture); - Ref<Gradient> get_gradient() const; - - void set_emissor_offset(const Point2 &p_offset); - Point2 get_emissor_offset() const; + void set_normal_map(const Ref<Texture> &p_normal_map); + Ref<Texture> get_normal_map() const; - void set_use_local_space(bool p_use); - bool is_using_local_space() const; - - void set_initial_velocity(const Vector2 &p_velocity); - Vector2 get_initial_velocity() const; + virtual String get_configuration_warning() const; - void set_emission_points(const PoolVector<Vector2> &p_points); - PoolVector<Vector2> get_emission_points() const; + void set_v_frames(int p_count); + int get_v_frames() const; - void pre_process(float p_delta); - void reset(); + void set_h_frames(int p_count); + int get_h_frames() const; + void restart(); + Rect2 capture_rect() const; Particles2D(); + ~Particles2D(); }; -VARIANT_ENUM_CAST(Particles2D::ProcessMode); -VARIANT_ENUM_CAST(Particles2D::Parameter); +VARIANT_ENUM_CAST(Particles2D::DrawOrder) #endif // PARTICLES_FRAME_H diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 68270ed771..fd261117e1 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -44,28 +44,6 @@ void PhysicsBody2D::_notification(int p_what) { */ } -void PhysicsBody2D::set_one_way_collision_direction(const Vector2 &p_dir) { - - one_way_collision_direction = p_dir; - Physics2DServer::get_singleton()->body_set_one_way_collision_direction(get_rid(), p_dir); -} - -Vector2 PhysicsBody2D::get_one_way_collision_direction() const { - - return one_way_collision_direction; -} - -void PhysicsBody2D::set_one_way_collision_max_depth(float p_depth) { - - one_way_collision_max_depth = p_depth; - Physics2DServer::get_singleton()->body_set_one_way_collision_max_depth(get_rid(), p_depth); -} - -float PhysicsBody2D::get_one_way_collision_max_depth() const { - - return one_way_collision_max_depth; -} - void PhysicsBody2D::_set_layers(uint32_t p_mask) { set_collision_layer(p_mask); @@ -92,10 +70,6 @@ void PhysicsBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers); ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers); - ClassDB::bind_method(D_METHOD("set_one_way_collision_direction", "dir"), &PhysicsBody2D::set_one_way_collision_direction); - ClassDB::bind_method(D_METHOD("get_one_way_collision_direction"), &PhysicsBody2D::get_one_way_collision_direction); - ClassDB::bind_method(D_METHOD("set_one_way_collision_max_depth", "depth"), &PhysicsBody2D::set_one_way_collision_max_depth); - ClassDB::bind_method(D_METHOD("get_one_way_collision_max_depth"), &PhysicsBody2D::get_one_way_collision_max_depth); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::remove_collision_exception_with); ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat @@ -103,9 +77,6 @@ void PhysicsBody2D::_bind_methods() { ADD_GROUP("Collision", "collision_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); - ADD_GROUP("", ""); - ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "one_way_collision/direction"), "set_one_way_collision_direction", "get_one_way_collision_direction"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "one_way_collision/max_depth"), "set_one_way_collision_max_depth", "get_one_way_collision_max_depth"); } void PhysicsBody2D::set_collision_layer(uint32_t p_layer) { @@ -164,7 +135,6 @@ PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) collision_layer = 1; collision_mask = 1; - set_one_way_collision_max_depth(0); set_pickable(false); } @@ -971,248 +941,105 @@ RigidBody2D::~RigidBody2D() { ////////////////////////// -Variant KinematicBody2D::_get_collider() const { - - ObjectID oid = get_collider(); - if (oid == 0) - return Variant(); - Object *obj = ObjectDB::get_instance(oid); - if (!obj) - return Variant(); - - Reference *ref = obj->cast_to<Reference>(); - if (ref) { - return Ref<Reference>(ref); - } - - return obj; -} - -void KinematicBody2D::revert_motion() { +Dictionary KinematicBody2D::_move(const Vector2 &p_motion) { + + Collision col; + if (move(p_motion, col)) { + Dictionary d; + d["position"] = col.collision; + d["normal"] = col.collision; + d["local_shape"] = col.local_shape; + d["travel"] = col.travel; + d["remainder"] = col.remainder; + d["collider_id"] = col.collider; + if (col.collider) { + d["collider"] = ObjectDB::get_instance(col.collider); + } else { + d["collider"] = Variant(); + } - Transform2D gt = get_global_transform(); - gt.elements[2] -= travel; - travel = Vector2(); - set_global_transform(gt); -} + d["collider_shape_index"] = col.collider_shape; + d["collider_metadata"] = col.collider_metadata; -Vector2 KinematicBody2D::get_travel() const { + return d; - return travel; + } else { + return Dictionary(); + } } -Vector2 KinematicBody2D::move(const Vector2 &p_motion) { - -#if 1 +bool KinematicBody2D::move(const Vector2 &p_motion, Collision &r_collision) { Transform2D gt = get_global_transform(); Physics2DServer::MotionResult result; - colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result); - - collider_metadata = result.collider_metadata; - collider_shape = result.collider_shape; - collider_vel = result.collider_velocity; - collision = result.collision_point; - normal = result.collision_normal; - collider = result.collider_id; - - gt.elements[2] += result.motion; - set_global_transform(gt); - travel = result.motion; - - return result.remainder; - -#else - //give me back regular physics engine logic - //this is madness - //and most people using this function will think - //what it does is simpler than using physics - //this took about a week to get right.. - //but is it right? who knows at this point.. - - colliding = false; - ERR_FAIL_COND_V(!is_inside_tree(), Vector2()); - Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space()); - ERR_FAIL_COND_V(!dss, Vector2()); - const int max_shapes = 32; - Vector2 sr[max_shapes * 2]; - int res_shapes; - - Set<RID> exclude; - exclude.insert(get_rid()); - - //recover first - int recover_attempts = 4; - - bool collided = false; - uint32_t mask = 0; - if (true) - mask |= Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY; - if (true) - mask |= Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY; - if (true) - mask |= Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY; - if (true) - mask |= Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY; - - //print_line("margin: "+rtos(margin)); - do { - - //motion recover - for (int i = 0; i < get_shape_count(); i++) { - - if (is_shape_set_as_trigger(i)) - continue; - if (dss->collide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), Vector2(), margin, sr, max_shapes, res_shapes, exclude, get_layer_mask(), mask)) - collided = true; - } - - if (!collided) - break; - - Vector2 recover_motion; - - for (int i = 0; i < res_shapes; i++) { - - Vector2 a = sr[i * 2 + 0]; - Vector2 b = sr[i * 2 + 1]; - - float d = a.distance_to(b); - - /* - if (d<margin) - continue; - */ - recover_motion += (b - a) * 0.4; - } - - if (recover_motion == Vector2()) { - collided = false; - break; - } - - Transform2D gt = get_global_transform(); - gt.elements[2] += recover_motion; - set_global_transform(gt); - - recover_attempts--; - - } while (recover_attempts); - - //move second - float safe = 1.0; - float unsafe = 1.0; - int best_shape = -1; - - for (int i = 0; i < get_shape_count(); i++) { - - if (is_shape_set_as_trigger(i)) - continue; - - float lsafe, lunsafe; - bool valid = dss->cast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion, 0, lsafe, lunsafe, exclude, get_layer_mask(), mask); - //print_line("shape: "+itos(i)+" travel:"+rtos(ltravel)); - if (!valid) { - - safe = 0; - unsafe = 0; - best_shape = i; //sadly it's the best - break; - } - if (lsafe == 1.0) { - continue; - } - if (lsafe < safe) { - - safe = lsafe; - unsafe = lunsafe; - best_shape = i; - } + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result); + + if (colliding) { + r_collision.collider_metadata = result.collider_metadata; + r_collision.collider_shape = result.collider_shape; + r_collision.collider_vel = result.collider_velocity; + r_collision.collision = result.collision_point; + r_collision.normal = result.collision_normal; + r_collision.collider = result.collider_id; + r_collision.travel = result.motion; + r_collision.remainder = result.remainder; + r_collision.local_shape = result.collision_local_shape; } - //print_line("best shape: "+itos(best_shape)+" motion "+p_motion); - - if (safe >= 1) { - //not collided - colliding = false; - - } else { - - //it collided, let's get the rest info in unsafe advance - Transform2D ugt = get_global_transform(); - ugt.elements[2] += p_motion * unsafe; - Physics2DDirectSpaceState::ShapeRestInfo rest_info; - bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt * get_shape_transform(best_shape), Vector2(), margin, &rest_info, exclude, get_layer_mask(), mask); - if (!c2) { - //should not happen, but floating point precision is so weird.. - - colliding = false; - } else { - - //print_line("Travel: "+rtos(travel)); - colliding = true; - collision = rest_info.point; - normal = rest_info.normal; - collider = rest_info.collider_id; - collider_vel = rest_info.linear_velocity; - collider_shape = rest_info.shape; - collider_metadata = rest_info.metadata; - } - } - - Vector2 motion = p_motion * safe; - Transform2D gt = get_global_transform(); - gt.elements[2] += motion; + gt.elements[2] += result.motion; set_global_transform(gt); - return p_motion - motion; -#endif + return colliding; } Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_bounces, float p_floor_max_angle) { - Vector2 motion = (move_and_slide_floor_velocity + p_linear_velocity) * get_fixed_process_delta_time(); + Vector2 motion = (floor_velocity + p_linear_velocity) * get_fixed_process_delta_time(); Vector2 lv = p_linear_velocity; - move_and_slide_on_floor = false; - move_and_slide_on_ceiling = false; - move_and_slide_on_wall = false; - move_and_slide_colliders.clear(); - move_and_slide_floor_velocity = Vector2(); + on_floor = false; + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector2(); while (p_max_bounces) { - motion = move(motion); + Collision collision; + + bool collided = move(motion, collision); - if (is_colliding()) { + if (collided) { + + motion = collision.remainder; if (p_floor_direction == Vector2()) { //all is a wall - move_and_slide_on_wall = true; + on_wall = true; } else { - if (get_collision_normal().dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor - move_and_slide_on_floor = true; - move_and_slide_floor_velocity = get_collider_velocity(); + on_floor = true; + floor_velocity = collision.collider_vel; - if (get_travel().length() < 1 && ABS((lv.x - move_and_slide_floor_velocity.x)) < p_slope_stop_min_velocity) { - revert_motion(); + if (collision.travel.length() < 1 && ABS((lv.x - floor_velocity.x)) < p_slope_stop_min_velocity) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); return Vector2(); } - } else if (get_collision_normal().dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling - move_and_slide_on_ceiling = true; + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + on_ceiling = true; } else { - move_and_slide_on_wall = true; + on_wall = true; } } - Vector2 n = get_collision_normal(); + Vector2 n = collision.normal; motion = motion.slide(n); lv = lv.slide(n); - Variant collider = _get_collider(); - if (collider.get_type() != Variant::NIL) { - move_and_slide_colliders.push_back(collider); - } + + colliders.push_back(collision); } else { break; @@ -1226,26 +1053,22 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const return lv; } -bool KinematicBody2D::is_move_and_slide_on_floor() const { +bool KinematicBody2D::is_on_floor() const { - return move_and_slide_on_floor; + return on_floor; } -bool KinematicBody2D::is_move_and_slide_on_wall() const { +bool KinematicBody2D::is_on_wall() const { - return move_and_slide_on_wall; + return on_wall; } -bool KinematicBody2D::is_move_and_slide_on_ceiling() const { +bool KinematicBody2D::is_on_ceiling() const { - return move_and_slide_on_ceiling; + return on_ceiling; } -Array KinematicBody2D::get_move_and_slide_colliders() const { - return move_and_slide_colliders; -} +Vector2 KinematicBody2D::get_floor_velocity() const { -Vector2 KinematicBody2D::move_to(const Vector2 &p_position) { - - return move(p_position - get_global_position()); + return floor_velocity; } bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion) { @@ -1255,98 +1078,123 @@ bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_moti return Physics2DServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, margin); } -Vector2 KinematicBody2D::get_collision_pos() const { +void KinematicBody2D::set_safe_margin(float p_margin) { - ERR_FAIL_COND_V(!colliding, Vector2()); - return collision; + margin = p_margin; } -Vector2 KinematicBody2D::get_collision_normal() const { +float KinematicBody2D::get_safe_margin() const { - ERR_FAIL_COND_V(!colliding, Vector2()); - return normal; + return margin; } -Vector2 KinematicBody2D::get_collider_velocity() const { +int KinematicBody2D::get_collision_count() const { - return collider_vel; + return colliders.size(); } +Vector2 KinematicBody2D::get_collision_position(int p_collision) const { -ObjectID KinematicBody2D::get_collider() const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2()); - ERR_FAIL_COND_V(!colliding, 0); - return collider; + return colliders[p_collision].collision; } - -int KinematicBody2D::get_collider_shape() const { - - ERR_FAIL_COND_V(!colliding, 0); - return collider_shape; +Vector2 KinematicBody2D::get_collision_normal(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2()); + return colliders[p_collision].normal; } -Variant KinematicBody2D::get_collider_metadata() const { - - ERR_FAIL_COND_V(!colliding, 0); - return collider_metadata; +Vector2 KinematicBody2D::get_collision_travel(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2()); + return colliders[p_collision].travel; } - -bool KinematicBody2D::is_colliding() const { - - return colliding; +Vector2 KinematicBody2D::get_collision_remainder(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2()); + return colliders[p_collision].remainder; } +Object *KinematicBody2D::get_collision_local_shape(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL); + uint32_t owner = shape_find_owner(colliders[p_collision].local_shape); + return shape_owner_get_owner(owner); +} +Object *KinematicBody2D::get_collision_collider(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL); -void KinematicBody2D::set_collision_margin(float p_margin) { + if (colliders[p_collision].collider) { + return ObjectDB::get_instance(colliders[p_collision].collider); + } - margin = p_margin; + return NULL; } +ObjectID KinematicBody2D::get_collision_collider_id(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), 0); -float KinematicBody2D::get_collision_margin() const { + return colliders[p_collision].collider; +} +Object *KinematicBody2D::get_collision_collider_shape(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL); + Object *collider = get_collision_collider(p_collision); + if (collider) { + CollisionObject2D *obj2d = collider->cast_to<CollisionObject2D>(); + if (obj2d) { + uint32_t owner = shape_find_owner(colliders[p_collision].collider_shape); + return obj2d->shape_owner_get_owner(owner); + } + } - return margin; + return NULL; +} +int KinematicBody2D::get_collision_collider_shape_index(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), -1); + return colliders[p_collision].collider_shape; +} +Vector2 KinematicBody2D::get_collision_collider_velocity(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2()); + return colliders[p_collision].collider_vel; +} +Variant KinematicBody2D::get_collision_collider_metadata(int p_collision) const { + ERR_FAIL_INDEX_V(p_collision, colliders.size(), Variant()); + return colliders[p_collision].collider_metadata; } void KinematicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::move); - ClassDB::bind_method(D_METHOD("move_to", "position"), &KinematicBody2D::move_to); + ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::_move); ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec"), &KinematicBody2D::test_move); - ClassDB::bind_method(D_METHOD("get_travel"), &KinematicBody2D::get_travel); - ClassDB::bind_method(D_METHOD("revert_motion"), &KinematicBody2D::revert_motion); - ClassDB::bind_method(D_METHOD("is_colliding"), &KinematicBody2D::is_colliding); + ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor); + ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling); + ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody2D::is_on_wall); + ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody2D::get_floor_velocity); - ClassDB::bind_method(D_METHOD("get_collision_pos"), &KinematicBody2D::get_collision_pos); - ClassDB::bind_method(D_METHOD("get_collision_normal"), &KinematicBody2D::get_collision_normal); - ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicBody2D::get_collider_velocity); - ClassDB::bind_method(D_METHOD("get_collider:Variant"), &KinematicBody2D::_get_collider); - ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicBody2D::get_collider_shape); - ClassDB::bind_method(D_METHOD("get_collider_metadata:Variant"), &KinematicBody2D::get_collider_metadata); - ClassDB::bind_method(D_METHOD("get_move_and_slide_colliders"), &KinematicBody2D::get_move_and_slide_colliders); - ClassDB::bind_method(D_METHOD("is_move_and_slide_on_floor"), &KinematicBody2D::is_move_and_slide_on_floor); - ClassDB::bind_method(D_METHOD("is_move_and_slide_on_ceiling"), &KinematicBody2D::is_move_and_slide_on_ceiling); - ClassDB::bind_method(D_METHOD("is_move_and_slide_on_wall"), &KinematicBody2D::is_move_and_slide_on_wall); + ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody2D::set_safe_margin); + ClassDB::bind_method(D_METHOD("get_safe_margin", "pixels"), &KinematicBody2D::get_safe_margin); - ClassDB::bind_method(D_METHOD("set_collision_margin", "pixels"), &KinematicBody2D::set_collision_margin); - ClassDB::bind_method(D_METHOD("get_collision_margin", "pixels"), &KinematicBody2D::get_collision_margin); + ClassDB::bind_method(D_METHOD("get_collision_count"), &KinematicBody2D::get_collision_count); + ClassDB::bind_method(D_METHOD("get_collision_position", "collision"), &KinematicBody2D::get_collision_position); + ClassDB::bind_method(D_METHOD("get_collision_normal", "collision"), &KinematicBody2D::get_collision_normal); + ClassDB::bind_method(D_METHOD("get_collision_travel", "collision"), &KinematicBody2D::get_collision_travel); + ClassDB::bind_method(D_METHOD("get_collision_remainder", "collision"), &KinematicBody2D::get_collision_remainder); + ClassDB::bind_method(D_METHOD("get_collision_local_shape", "collision"), &KinematicBody2D::get_collision_local_shape); + ClassDB::bind_method(D_METHOD("get_collision_collider", "collision"), &KinematicBody2D::get_collision_collider); + ClassDB::bind_method(D_METHOD("get_collision_collider_id", "collision"), &KinematicBody2D::get_collision_collider_id); + ClassDB::bind_method(D_METHOD("get_collision_collider_shape", "collision"), &KinematicBody2D::get_collision_collider_shape); + ClassDB::bind_method(D_METHOD("get_collision_collider_shape_index", "collision"), &KinematicBody2D::get_collision_collider_shape_index); + ClassDB::bind_method(D_METHOD("get_collision_collider_velocity", "collision"), &KinematicBody2D::get_collision_collider_velocity); + ClassDB::bind_method(D_METHOD("get_collision_collider_metadata", "collision"), &KinematicBody2D::get_collision_collider_metadata); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_collision_margin", "get_collision_margin"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } KinematicBody2D::KinematicBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC) { - colliding = false; - collider = 0; - - collider_shape = 0; - margin = 0.08; - move_and_slide_on_floor = false; - move_and_slide_on_ceiling = false; - move_and_slide_on_wall = false; + on_floor = false; + on_ceiling = false; + on_wall = false; } KinematicBody2D::~KinematicBody2D() { } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 50c9865f18..8c8e4ebc77 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -40,8 +40,6 @@ class PhysicsBody2D : public CollisionObject2D { uint32_t collision_layer; uint32_t collision_mask; - Vector2 one_way_collision_direction; - float one_way_collision_max_depth; void _set_layers(uint32_t p_mask); uint32_t _get_layers() const; @@ -68,12 +66,6 @@ public: void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); - void set_one_way_collision_direction(const Vector2 &p_dir); - Vector2 get_one_way_collision_direction() const; - - void set_one_way_collision_max_depth(float p_dir); - float get_one_way_collision_max_depth() const; - PhysicsBody2D(); }; @@ -272,54 +264,60 @@ class KinematicBody2D : public PhysicsBody2D { GDCLASS(KinematicBody2D, PhysicsBody2D); +public: + struct Collision { + Vector2 collision; + Vector2 normal; + Vector2 collider_vel; + ObjectID collider; + int collider_shape; + Variant collider_metadata; + Vector2 remainder; + Vector2 travel; + int local_shape; + }; + +private: float margin; - bool colliding; - Vector2 collision; - Vector2 normal; - Vector2 collider_vel; - ObjectID collider; - int collider_shape; - Variant collider_metadata; - Vector2 travel; - - Vector2 move_and_slide_floor_velocity; - bool move_and_slide_on_floor; - bool move_and_slide_on_ceiling; - bool move_and_slide_on_wall; - Array move_and_slide_colliders; - - Variant _get_collider() const; + + Vector2 floor_velocity; + bool on_floor; + bool on_ceiling; + bool on_wall; + Vector<Collision> colliders; _FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const; + Dictionary _move(const Vector2 &p_motion); + protected: static void _bind_methods(); public: - Vector2 move(const Vector2 &p_motion); - Vector2 move_to(const Vector2 &p_position); - + bool move(const Vector2 &p_motion, Collision &r_collision); bool test_move(const Transform2D &p_from, const Vector2 &p_motion); - bool is_colliding() const; - - Vector2 get_travel() const; - void revert_motion(); - - Vector2 get_collision_pos() const; - Vector2 get_collision_normal() const; - Vector2 get_collider_velocity() const; - ObjectID get_collider() const; - int get_collider_shape() const; - Variant get_collider_metadata() const; - void set_collision_margin(float p_margin); - float get_collision_margin() const; + void set_safe_margin(float p_margin); + float get_safe_margin() const; Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), float p_slope_stop_min_velocity = 5, int p_max_bounces = 4, float p_floor_max_angle = Math::deg2rad((float)45)); - bool is_move_and_slide_on_floor() const; - bool is_move_and_slide_on_wall() const; - bool is_move_and_slide_on_ceiling() const; - Array get_move_and_slide_colliders() const; + bool is_on_floor() const; + bool is_on_wall() const; + bool is_on_ceiling() const; + Vector2 get_floor_velocity() const; + + int get_collision_count() const; + Vector2 get_collision_position(int p_collision) const; + Vector2 get_collision_normal(int p_collision) const; + Vector2 get_collision_travel(int p_collision) const; + Vector2 get_collision_remainder(int p_collision) const; + Object *get_collision_local_shape(int p_collision) const; + Object *get_collision_collider(int p_collision) const; + ObjectID get_collision_collider_id(int p_collision) const; + Object *get_collision_collider_shape(int p_collision) const; + int get_collision_collider_shape_index(int p_collision) const; + Vector2 get_collision_collider_velocity(int p_collision) const; + Variant get_collision_collider_metadata(int p_collision) const; KinematicBody2D(); ~KinematicBody2D(); diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index c1eb90e52a..450f8e2474 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -65,11 +65,13 @@ void Sprite::_notification(int p_what) { Size2 s; Rect2 src_rect; + bool filter_clip = false; if (region) { s = region_rect.size; src_rect = region_rect; + filter_clip = region_filter_clip; } else { s = Size2(texture->get_size()); s = s / Size2(hframes, vframes); @@ -93,7 +95,7 @@ void Sprite::_notification(int p_what) { if (vflip) dst_rect.size.y = -dst_rect.size.y; - texture->draw_rect_region(ci, dst_rect, src_rect); + texture->draw_rect_region(ci, dst_rect, src_rect, Color(1, 1, 1), false, normal_map, filter_clip); } break; } @@ -109,17 +111,30 @@ void Sprite::set_texture(const Ref<Texture> &p_texture) { } #endif texture = p_texture; + /* this should no longer be needed in 3.0 #ifdef DEBUG_ENABLED if (texture.is_valid()) { texture->set_flags(texture->get_flags()); //remove repeat from texture, it looks bad in sprites texture->connect(CoreStringNames::get_singleton()->changed, this, SceneStringNames::get_singleton()->update); } #endif +*/ update(); emit_signal("texture_changed"); item_rect_changed(); } +void Sprite::set_normal_map(const Ref<Texture> &p_texture) { + + normal_map = p_texture; + update(); +} + +Ref<Texture> Sprite::get_normal_map() const { + + return normal_map; +} + Ref<Texture> Sprite::get_texture() const { return texture; @@ -201,6 +216,15 @@ Rect2 Sprite::get_region_rect() const { return region_rect; } +void Sprite::set_region_filter_clip(bool p_enable) { + region_filter_clip = p_enable; + update(); +} + +bool Sprite::is_region_filter_clip_enabled() const { + return region_filter_clip; +} + void Sprite::set_frame(int p_frame) { ERR_FAIL_INDEX(p_frame, vframes * hframes); @@ -289,6 +313,9 @@ void Sprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture:Texture"), &Sprite::set_texture); ClassDB::bind_method(D_METHOD("get_texture:Texture"), &Sprite::get_texture); + ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map:Texture"), &Sprite::set_normal_map); + ClassDB::bind_method(D_METHOD("get_normal_map:Texture"), &Sprite::get_normal_map); + ClassDB::bind_method(D_METHOD("set_centered", "centered"), &Sprite::set_centered); ClassDB::bind_method(D_METHOD("is_centered"), &Sprite::is_centered); @@ -307,6 +334,9 @@ void Sprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_region_rect", "rect"), &Sprite::set_region_rect); ClassDB::bind_method(D_METHOD("get_region_rect"), &Sprite::get_region_rect); + ClassDB::bind_method(D_METHOD("set_region_filter_clip", "enabled"), &Sprite::set_region_filter_clip); + ClassDB::bind_method(D_METHOD("is_region_filter_clip_enabled"), &Sprite::is_region_filter_clip_enabled); + ClassDB::bind_method(D_METHOD("set_frame", "frame"), &Sprite::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &Sprite::get_frame); @@ -320,15 +350,21 @@ void Sprite::_bind_methods() { ADD_SIGNAL(MethodInfo("texture_changed")); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map"); + ADD_GROUP("Offset", ""); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v"); + ADD_GROUP("Animation", ""); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "region"), "set_region", "is_region"); + + ADD_GROUP("Region", "region_"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region"); ADD_PROPERTYNZ(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "region_filter_clip"), "set_region_filter_clip", "is_region_filter_clip_enabled"); } Sprite::Sprite() { @@ -337,6 +373,7 @@ Sprite::Sprite() { hflip = false; vflip = false; region = false; + region_filter_clip = false; frame = 0; diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index 86ef335d42..d3f9a5f032 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -38,6 +38,7 @@ class Sprite : public Node2D { GDCLASS(Sprite, Node2D); Ref<Texture> texture; + Ref<Texture> normal_map; bool centered; Point2 offset; @@ -46,6 +47,7 @@ class Sprite : public Node2D { bool vflip; bool region; Rect2 region_rect; + bool region_filter_clip; int frame; @@ -67,6 +69,9 @@ public: 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; + void set_centered(bool p_center); bool is_centered() const; @@ -82,6 +87,9 @@ public: void set_region(bool p_region); bool is_region() const; + void set_region_filter_clip(bool p_enable); + bool is_region_filter_clip_enabled() const; + void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 4f892a31fc..57e25ec609 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "tile_map.h" #include "io/marshalls.h" -#include "method_bind_ext.inc" +#include "method_bind_ext.gen.inc" #include "os/os.h" #include "servers/physics_2d_server.h" @@ -441,35 +441,36 @@ void TileMap::_update_dirty_quadrants() { rect.position.y -= center.y; } + Ref<Texture> normal_map = tile_set->tile_get_normal_map(c.id); Color modulate = tile_set->tile_get_modulate(c.id); Color self_modulate = get_self_modulate(); modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, modulate.b * self_modulate.b, modulate.a * self_modulate.a); if (r == Rect2()) { - tex->draw_rect(canvas_item, rect, false, modulate, c.transpose); + tex->draw_rect(canvas_item, rect, false, modulate, c.transpose, normal_map); } else { - tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose); + tex->draw_rect_region(canvas_item, rect, r, modulate, c.transpose, normal_map); } - Vector<Ref<Shape2D> > shapes = tile_set->tile_get_shapes(c.id); + Vector<TileSet::ShapeData> shapes = tile_set->tile_get_shapes(c.id); for (int i = 0; i < shapes.size(); i++) { - Ref<Shape2D> shape = shapes[i]; + Ref<Shape2D> shape = shapes[i].shape; if (shape.is_valid()) { - - Vector2 shape_ofs = tile_set->tile_get_shape_offset(c.id); Transform2D xform; xform.set_origin(offset.floor()); - _fix_cell_transform(xform, c, shape_ofs + center_ofs, s); + _fix_cell_transform(xform, c, shapes[i].shape_offset + center_ofs, s); if (debug_canvas_item.is_valid()) { vs->canvas_item_add_set_transform(debug_canvas_item, xform); shape->draw(debug_canvas_item, debug_collision_color); } 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)); + shape_idx++; + ps->body_set_shape_as_one_way_collision(q.body, shape_idx, shapes[i].one_way_collision); + ps->body_set_shape_metadata(q.body, shape_idx, Vector2(E->key().x, E->key().y)); } } |