diff options
Diffstat (limited to 'scene')
145 files changed, 10009 insertions, 1104 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index e3d1592be0..85e7f8df92 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -192,6 +192,16 @@ void SpriteFrames::get_animation_list(List<StringName> *r_animations) const { } } +Vector<String> SpriteFrames::get_animation_names() const { + + Vector<String> names; + for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) { + names.push_back(E->key()); + } + names.sort(); + return names; +} + void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) { ERR_FAIL_COND(p_fps < 0); @@ -225,7 +235,7 @@ void SpriteFrames::_set_frames(const Array &p_frames) { E->get().frames.resize(p_frames.size()); for (int i = 0; i < E->get().frames.size(); i++) - E->get().frames[i] = p_frames[i]; + E->get().frames.write[i] = p_frames[i]; } Array SpriteFrames::_get_frames() const { @@ -283,6 +293,8 @@ void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation); ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation); + ClassDB::bind_method(D_METHOD("get_animation_names"), &SpriteFrames::get_animation_names); + ClassDB::bind_method(D_METHOD("set_animation_speed", "anim", "speed"), &SpriteFrames::set_animation_speed); ClassDB::bind_method(D_METHOD("get_animation_speed", "anim"), &SpriteFrames::get_animation_speed); diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 806052a696..cc49465403 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -72,6 +72,7 @@ public: void rename_animation(const StringName &p_prev, const StringName &p_next); void get_animation_list(List<StringName> *r_animations) const; + Vector<String> get_animation_names() const; void set_animation_speed(const StringName &p_anim, float p_fps); float get_animation_speed(const StringName &p_anim) const; @@ -112,7 +113,7 @@ public: ERR_FAIL_COND(p_idx < 0); if (p_idx >= E->get().frames.size()) return; - E->get().frames[p_idx] = p_frame; + E->get().frames.write[p_idx] = p_frame; } void remove_frame(const StringName &p_anim, int p_idx); void clear(const StringName &p_anim); diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp index b6dfc666b0..507499a324 100644 --- a/scene/2d/audio_stream_player_2d.cpp +++ b/scene/2d/audio_stream_player_2d.cpp @@ -37,7 +37,7 @@ void AudioStreamPlayer2D::_mix_audio() { if (!stream_playback.is_valid() || !active || - (stream_paused && stream_paused_fade <= 0.f)) { + (stream_paused && !stream_paused_fade_out)) { return; } @@ -50,11 +50,13 @@ void AudioStreamPlayer2D::_mix_audio() { AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); - // Mix if we're not paused or we're fading out - if (!stream_paused || stream_paused_fade > 0.f) { - stream_playback->mix(buffer, pitch_scale, buffer_size); + if (stream_paused_fade_out) { + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); } + stream_playback->mix(buffer, pitch_scale, buffer_size); + //write all outputs for (int i = 0; i < output_count; i++) { @@ -82,15 +84,10 @@ void AudioStreamPlayer2D::_mix_audio() { } //mix! - AudioFrame vol_inc = (current.vol - prev_outputs[i].vol) / float(buffer_size); - AudioFrame vol = current.vol; - - if (stream_paused) { - vol = vol * stream_paused_fade; - if (stream_paused_fade > 0.f) { - stream_paused_fade -= 0.1f; - } - } + AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol; + AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol; + AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); + AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol; int cc = AudioServer::get_singleton()->get_channel_count(); @@ -131,6 +128,8 @@ void AudioStreamPlayer2D::_mix_audio() { } output_ready = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; } void AudioStreamPlayer2D::_notification(int p_what) { @@ -305,6 +304,11 @@ float AudioStreamPlayer2D::get_pitch_scale() const { void AudioStreamPlayer2D::play(float p_from_pos) { + if (!is_playing()) { + // Reset the prev_output_count if the stream is stopped + prev_output_count = 0; + } + if (stream_playback.is_valid()) { setplay = p_from_pos; output_ready = false; @@ -439,7 +443,8 @@ void AudioStreamPlayer2D::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_paused_fade = stream_paused ? 1.f : 0.f; + stream_paused_fade_in = p_pause ? false : true; + stream_paused_fade_out = p_pause ? true : false; } } @@ -518,7 +523,8 @@ AudioStreamPlayer2D::AudioStreamPlayer2D() { output_ready = false; area_mask = 1; stream_paused = false; - stream_paused_fade = 0.f; + stream_paused_fade_in = false; + stream_paused_fade_out = false; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h index b508de3171..e68e6eeca5 100644 --- a/scene/2d/audio_stream_player_2d.h +++ b/scene/2d/audio_stream_player_2d.h @@ -71,9 +71,10 @@ private: float volume_db; float pitch_scale; - float stream_paused_fade; bool autoplay; bool stream_paused; + bool stream_paused_fade_in; + bool stream_paused_fade_out; StringName bus; void _mix_audio(); diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 47326b9be2..a035d9021f 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -411,6 +411,9 @@ void CanvasItem::_enter_canvas() { if (canvas_layer) { break; } + if (Object::cast_to<Viewport>(n)) { + break; + } n = n->get_parent(); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index d05c818ae1..1e2184bd41 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -38,10 +38,14 @@ void CollisionObject2D::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + Transform2D global_transform = get_global_transform(); + if (area) - Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform()); + Physics2DServer::get_singleton()->area_set_transform(rid, global_transform); else - Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform()); + Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform); + + last_transform = global_transform; RID space = get_world_2d()->get_space(); if (area) { @@ -60,10 +64,18 @@ void CollisionObject2D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { + Transform2D global_transform = get_global_transform(); + + if (only_update_transform_changes && global_transform == last_transform) { + return; + } + if (area) - Physics2DServer::get_singleton()->area_set_transform(rid, get_global_transform()); + Physics2DServer::get_singleton()->area_set_transform(rid, global_transform); else - Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, get_global_transform()); + Physics2DServer::get_singleton()->body_set_state(rid, Physics2DServer::BODY_STATE_TRANSFORM, global_transform); + + last_transform = global_transform; } break; case NOTIFICATION_EXIT_TREE: { @@ -247,7 +259,7 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int 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; + E->get().shapes.write[i].index -= 1; } } } @@ -318,6 +330,10 @@ void CollisionObject2D::_mouse_exit() { emit_signal(SceneStringNames::get_singleton()->mouse_exited); } +void CollisionObject2D::set_only_update_transform_changes(bool p_enable) { + only_update_transform_changes = p_enable; +} + void CollisionObject2D::_update_pickable() { if (!is_inside_tree()) return; @@ -384,6 +400,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) { pickable = true; set_notify_transform(true); total_subshapes = 0; + only_update_transform_changes = false; if (p_area) { diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index 6da63d1a0b..29a00bd9f9 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -65,6 +65,8 @@ class CollisionObject2D : public Node2D { int total_subshapes; Map<uint32_t, ShapeData> shapes; + Transform2D last_transform; + bool only_update_transform_changes; //this is used for sync physics in KinematicBody protected: CollisionObject2D(RID p_rid, bool p_area); @@ -78,6 +80,8 @@ protected: void _mouse_enter(); void _mouse_exit(); + void set_only_update_transform_changes(bool p_enable); + public: uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index 9d2a83fda7..9f19f56e75 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -102,11 +102,11 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() { TriangulatorPoly &tp = I->get(); - decomp[idx].resize(tp.GetNumPoints()); + decomp.write[idx].resize(tp.GetNumPoints()); for (int i = 0; i < tp.GetNumPoints(); i++) { - decomp[idx][i] = tp.GetPoint(i); + decomp.write[idx].write[i] = tp.GetPoint(i); } idx++; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 3e61dd05f4..ad9775c0b7 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -239,7 +239,7 @@ void Line2D::_draw() { { PoolVector<Vector2>::Read points_read = _points.read(); for (int i = 0; i < len; ++i) { - points[i] = points_read[i]; + points.write[i] = points_read[i]; } } @@ -349,7 +349,7 @@ void Line2D::_bind_methods() { 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_PROPERTYNZ(PropertyInfo(Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile,Stretch"), "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"); @@ -368,6 +368,7 @@ void Line2D::_bind_methods() { BIND_ENUM_CONSTANT(LINE_TEXTURE_NONE); BIND_ENUM_CONSTANT(LINE_TEXTURE_TILE); + BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH); ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed); } diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 24c48982cd..6918018c12 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -52,8 +52,8 @@ public: enum LineTextureMode { LINE_TEXTURE_NONE = 0, - LINE_TEXTURE_TILE - // TODO STRETCH mode + LINE_TEXTURE_TILE, + LINE_TEXTURE_STRETCH }; Line2D(); diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 845788bada..a3f1b25e05 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -146,7 +146,9 @@ void LineBuilder::build() { float current_distance1 = 0.f; float total_distance = 0.f; _interpolate_color = gradient != NULL; - bool distance_required = _interpolate_color || texture_mode == Line2D::LINE_TEXTURE_TILE; + bool distance_required = _interpolate_color || + texture_mode == Line2D::LINE_TEXTURE_TILE || + texture_mode == Line2D::LINE_TEXTURE_STRETCH; if (distance_required) total_distance = calculate_total_distance(points); if (_interpolate_color) @@ -170,7 +172,7 @@ void LineBuilder::build() { if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx0 = 0.5f / tile_aspect; } - new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f)); + new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f)); total_distance += width; current_distance0 += hw; current_distance1 = current_distance0; @@ -290,8 +292,10 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { - uvx0 = current_distance0 / (width * tile_aspect); uvx1 = current_distance1 / (width * tile_aspect); + } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { + uvx0 = current_distance0 / total_distance; + uvx1 = current_distance1 / total_distance; } strip_add_quad(pos_up1, pos_down1, color1, uvx1); @@ -301,7 +305,6 @@ void LineBuilder::build() { u0 = u1; f0 = f1; pos0 = pos1; - current_distance0 = current_distance1; if (intersection_result == SEGMENT_INTERSECT) { if (current_joint_mode == Line2D::LINE_JOINT_SHARP) { pos_up0 = pos_up1; @@ -378,6 +381,8 @@ void LineBuilder::build() { } if (texture_mode == Line2D::LINE_TEXTURE_TILE) { uvx1 = current_distance1 / (width * tile_aspect); + } else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) { + uvx1 = current_distance1 / total_distance; } strip_add_quad(pos_up1, pos_down1, color1, uvx1); @@ -386,7 +391,7 @@ void LineBuilder::build() { if (end_cap_mode == Line2D::LINE_CAP_ROUND) { // Note: color is not used in case we don't interpolate... Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0); - new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f, 0.f, 1.f, 1.f)); + new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f)); } } diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp index 16e0386952..e3b048fd74 100644 --- a/scene/2d/navigation2d.cpp +++ b/scene/2d/navigation2d.cpp @@ -74,7 +74,7 @@ void Navigation2D::_navpoly_link(int p_id) { Vector2 ep = nm.xform.xform(r[idx]); center += ep; e.point = _get_point(ep); - p.edges[j] = e; + p.edges.write[j] = e; int idxn = indices[(j + 1) % plen]; if (idxn < 0 || idxn >= len) { @@ -119,17 +119,17 @@ void Navigation2D::_navpoly_link(int p_id) { ConnectionPending pending; pending.polygon = &p; pending.edge = j; - p.edges[j].P = C->get().pending.push_back(pending); + p.edges.write[j].P = C->get().pending.push_back(pending); continue; //print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b)); } C->get().B = &p; C->get().B_edge = j; - C->get().A->edges[C->get().A_edge].C = &p; - C->get().A->edges[C->get().A_edge].C_edge = j; - p.edges[j].C = C->get().A; - p.edges[j].C_edge = C->get().A_edge; + C->get().A->edges.write[C->get().A_edge].C = &p; + C->get().A->edges.write[C->get().A_edge].C_edge = j; + p.edges.write[j].C = C->get().A; + p.edges.write[j].C_edge = C->get().A_edge; //connection successful. } } @@ -167,10 +167,10 @@ void Navigation2D::_navpoly_unlink(int p_id) { } else if (C->get().B) { //disconnect - C->get().B->edges[C->get().B_edge].C = NULL; - C->get().B->edges[C->get().B_edge].C_edge = -1; - C->get().A->edges[C->get().A_edge].C = NULL; - C->get().A->edges[C->get().A_edge].C_edge = -1; + C->get().B->edges.write[C->get().B_edge].C = NULL; + C->get().B->edges.write[C->get().B_edge].C_edge = -1; + C->get().A->edges.write[C->get().A_edge].C = NULL; + C->get().A->edges.write[C->get().A_edge].C_edge = -1; if (C->get().A == &E->get()) { @@ -187,11 +187,11 @@ void Navigation2D::_navpoly_unlink(int p_id) { C->get().B = cp.polygon; C->get().B_edge = cp.edge; - C->get().A->edges[C->get().A_edge].C = cp.polygon; - C->get().A->edges[C->get().A_edge].C_edge = cp.edge; - cp.polygon->edges[cp.edge].C = C->get().A; - cp.polygon->edges[cp.edge].C_edge = C->get().A_edge; - cp.polygon->edges[cp.edge].P = NULL; + C->get().A->edges.write[C->get().A_edge].C = cp.polygon; + C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; + cp.polygon->edges.write[cp.edge].C = C->get().A; + cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; + cp.polygon->edges.write[cp.edge].P = NULL; } } else { @@ -339,8 +339,8 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect Vector<Vector2> path; path.resize(2); - path[0] = begin_point; - path[1] = end_point; + path.write[0] = begin_point; + path.write[1] = end_point; //print_line("Direct Path"); return path; } @@ -408,7 +408,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect for (int i = 0; i < es; i++) { - Polygon::Edge &e = p->edges[i]; + Polygon::Edge &e = p->edges.write[i]; if (!e.C) continue; @@ -586,7 +586,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (!path.size() || path[path.size() - 1].distance_squared_to(begin_point) > CMP_EPSILON) { path.push_back(begin_point); // Add the begin point } else { - path[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point + path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point } path.invert(); @@ -594,7 +594,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (path.size() <= 1 || path[path.size() - 1].distance_squared_to(end_point) > CMP_EPSILON) { path.push_back(end_point); // Add the end point } else { - path[path.size() - 1] = end_point; // Replace last midpoint by the exact end point + path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point } return path; diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index 6e27bf1c1d..2d6679272a 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -91,7 +91,7 @@ void NavigationPolygon::_set_polygons(const Array &p_array) { polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons[i].indices = p_array[i]; + polygons.write[i].indices = p_array[i]; } } @@ -110,7 +110,7 @@ void NavigationPolygon::_set_outlines(const Array &p_array) { outlines.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - outlines[i] = p_array[i]; + outlines.write[i] = p_array[i]; } rect_cache_dirty = true; } @@ -166,7 +166,7 @@ int NavigationPolygon::get_outline_count() const { void NavigationPolygon::set_outline(int p_idx, const PoolVector<Vector2> &p_outline) { ERR_FAIL_INDEX(p_idx, outlines.size()); - outlines[p_idx] = p_outline; + outlines.write[p_idx] = p_outline; rect_cache_dirty = true; } @@ -432,8 +432,8 @@ void NavigationPolygonInstance::_notification(int p_what) { { PoolVector<Vector2>::Read vr = verts.read(); for (int i = 0; i < vsize; i++) { - vertices[i] = vr[i]; - colors[i] = color; + vertices.write[i] = vr[i]; + colors.write[i] = color; } } diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 3813bd96fe..7252602a93 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -130,7 +130,6 @@ void Node2D::_update_xform_values() { void Node2D::_update_transform() { - Transform2D mat(angle, pos); _mat.set_rotation_and_scale(angle, _scale); _mat.elements[2] = pos; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index feb11089d0..8758ffef9f 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -30,10 +30,11 @@ #include "physics_body_2d.h" +#include "core/core_string_names.h" #include "core/method_bind_ext.gen.inc" #include "engine.h" +#include "math_funcs.h" #include "scene/scene_string_names.h" - void PhysicsBody2D::_notification(int p_what) { /* @@ -186,28 +187,76 @@ real_t StaticBody2D::get_constant_angular_velocity() const { return constant_angular_velocity; } +#ifndef DISABLE_DEPRECATED void StaticBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } + real_t StaticBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } + real_t StaticBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void StaticBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody2D::get_physics_material_override() const { + return physics_material_override; } void StaticBody2D::_bind_methods() { @@ -216,29 +265,48 @@ void StaticBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody2D::set_constant_angular_velocity); ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody2D::get_constant_linear_velocity); ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody2D::get_constant_angular_velocity); + +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &StaticBody2D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &StaticBody2D::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody2D::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody2D::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody2D::_reload_physics_characteristics); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); } StaticBody2D::StaticBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) { constant_angular_velocity = 0; - bounce = 0; - friction = 1; } StaticBody2D::~StaticBody2D() { } +void StaticBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + void RigidBody2D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); @@ -545,28 +613,73 @@ real_t RigidBody2D::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/2d/default_gravity", 98)) / 10; } +#ifndef DISABLE_DEPRECATED void RigidBody2D::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } real_t RigidBody2D::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody2D::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } real_t RigidBody2D::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void RigidBody2D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody2D::get_physics_material_override() const { + return physics_material_override; } void RigidBody2D::set_gravity_scale(real_t p_gravity_scale) { @@ -690,11 +803,19 @@ int RigidBody2D::get_max_contacts_reported() const { return max_contacts_reported; } +void RigidBody2D::apply_central_impulse(const Vector2 &p_impulse) { + Physics2DServer::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); +} + void RigidBody2D::apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse) { Physics2DServer::get_singleton()->body_apply_impulse(get_rid(), p_offset, p_impulse); } +void RigidBody2D::apply_torque_impulse(float p_torque) { + Physics2DServer::get_singleton()->body_apply_torque_impulse(get_rid(), p_torque); +} + void RigidBody2D::set_applied_force(const Vector2 &p_force) { Physics2DServer::get_singleton()->body_set_applied_force(get_rid(), p_force); @@ -715,11 +836,19 @@ float RigidBody2D::get_applied_torque() const { return Physics2DServer::get_singleton()->body_get_applied_torque(get_rid()); }; +void RigidBody2D::add_central_force(const Vector2 &p_force) { + Physics2DServer::get_singleton()->body_add_central_force(get_rid(), p_force); +} + void RigidBody2D::add_force(const Vector2 &p_offset, const Vector2 &p_force) { Physics2DServer::get_singleton()->body_add_force(get_rid(), p_offset, p_force); } +void RigidBody2D::add_torque(const float p_torque) { + Physics2DServer::get_singleton()->body_add_torque(get_rid(), p_torque); +} + void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) { ccd_mode = p_mode; @@ -837,11 +966,18 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody2D::set_weight); ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody2D::get_weight); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &RigidBody2D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &RigidBody2D::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody2D::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody2D::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody2D::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody2D::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody2D::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &RigidBody2D::set_gravity_scale); ClassDB::bind_method(D_METHOD("get_gravity_scale"), &RigidBody2D::get_gravity_scale); @@ -871,7 +1007,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_continuous_collision_detection_mode"), &RigidBody2D::get_continuous_collision_detection_mode); ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody2D::set_axis_velocity); + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody2D::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "offset", "impulse"), &RigidBody2D::apply_impulse); + ClassDB::bind_method(D_METHOD("apply_torque_impulse", "torque"), &RigidBody2D::apply_torque_impulse); ClassDB::bind_method(D_METHOD("set_applied_force", "force"), &RigidBody2D::set_applied_force); ClassDB::bind_method(D_METHOD("get_applied_force"), &RigidBody2D::get_applied_force); @@ -879,7 +1017,9 @@ void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_applied_torque", "torque"), &RigidBody2D::set_applied_torque); ClassDB::bind_method(D_METHOD("get_applied_torque"), &RigidBody2D::get_applied_torque); + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody2D::add_central_force); ClassDB::bind_method(D_METHOD("add_force", "offset", "force"), &RigidBody2D::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody2D::add_torque); ClassDB::bind_method(D_METHOD("set_sleeping", "sleeping"), &RigidBody2D::set_sleeping); ClassDB::bind_method(D_METHOD("is_sleeping"), &RigidBody2D::is_sleeping); @@ -901,8 +1041,11 @@ void RigidBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "inertia", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", 0), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::INT, "continuous_cd", PROPERTY_HINT_ENUM, "Disabled,Cast Ray,Cast Shape"), "set_continuous_collision_detection_mode", "get_continuous_collision_detection_mode"); @@ -941,9 +1084,7 @@ RigidBody2D::RigidBody2D() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; gravity_scale = 1; linear_damp = -1; @@ -969,13 +1110,23 @@ RigidBody2D::~RigidBody2D() { memdelete(contact_monitor); } +void RigidBody2D::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, 0); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, 1); + } else { + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + Physics2DServer::get_singleton()->body_set_param(get_rid(), Physics2DServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + ////////////////////////// -Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia) { +Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) { Collision col; - if (move_and_collide(p_motion, p_infinite_inertia, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -989,11 +1140,48 @@ Ref<KinematicCollision2D> KinematicBody2D::_move(const Vector2 &p_motion, bool p return Ref<KinematicCollision2D>(); } -bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision) { +bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { + + Physics2DServer::SeparationResult sep_res[8]; //max 8 rays + + Transform2D gt = get_global_transform(); + + Vector2 recover; + int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin); + int deepest = -1; + float deepest_depth; + for (int i = 0; i < hits; i++) { + if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) { + deepest = i; + deepest_depth = sep_res[i].collision_depth; + } + } + + gt.elements[2] += recover; + set_global_transform(gt); + + if (deepest != -1) { + r_collision.collider = sep_res[deepest].collider_id; + r_collision.collider_metadata = sep_res[deepest].collider_metadata; + r_collision.collider_shape = sep_res[deepest].collider_shape; + r_collision.collider_vel = sep_res[deepest].collider_velocity; + r_collision.collision = sep_res[deepest].collision_point; + r_collision.normal = sep_res[deepest].collision_normal; + r_collision.local_shape = sep_res[deepest].collision_local_shape; + r_collision.travel = recover; + r_collision.remainder = Vector2(); + + return true; + } else { + return false; + } +} + +bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) { Transform2D gt = get_global_transform(); Physics2DServer::MotionResult result; - bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result); + bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes); if (colliding) { r_collision.collider_metadata = result.collider_metadata; @@ -1002,23 +1190,39 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ r_collision.collision = result.collision_point; r_collision.normal = result.collision_normal; r_collision.collider = result.collider_id; + r_collision.collider_rid = result.collider; r_collision.travel = result.motion; r_collision.remainder = result.remainder; r_collision.local_shape = result.collision_local_shape; } - gt.elements[2] += result.motion; - set_global_transform(gt); + if (!p_test_only) { + gt.elements[2] += result.motion; + set_global_transform(gt); + } return colliding; } +//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +#define FLOOR_ANGLE_THRESHOLD 0.01 + Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { - Vector2 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time(); + Vector2 floor_motion = floor_velocity; + if (on_floor && on_floor_body.is_valid()) { + //this approach makes sure there is less delay between the actual body velocity and the one we saved + Physics2DDirectBodyState *bs = Physics2DServer::get_singleton()->body_get_direct_state(on_floor_body); + if (bs) { + floor_motion = bs->get_linear_velocity(); + } + } + + Vector2 motion = (floor_motion + p_linear_velocity) * get_physics_process_delta_time(); Vector2 lv = p_linear_velocity; on_floor = false; + on_floor_body = RID(); on_ceiling = false; on_wall = false; colliders.clear(); @@ -1027,48 +1231,68 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const while (p_max_slides) { Collision collision; + bool found_collision = false; + + for (int i = 0; i < 2; i++) { + bool collided; + if (i == 0) { //collide + collided = move_and_collide(motion, p_infinite_inertia, collision); + if (!collided) { + motion = Vector2(); //clear because no collision happened and motion completed + } + } else { //separate raycasts (if any) + collided = separate_raycast_shapes(p_infinite_inertia, collision); + if (collided) { + collision.remainder = motion; //keep + collision.travel = Vector2(); + } + } - bool collided = move_and_collide(motion, p_infinite_inertia, collision); - - if (collided) { - - motion = collision.remainder; - - if (p_floor_direction == Vector2()) { - //all is a wall - on_wall = true; - } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor + if (collided) { + found_collision = true; + } - on_floor = true; - floor_velocity = collision.collider_vel; + if (collided) { - Vector2 rel_v = lv - floor_velocity; - Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + motion = collision.remainder; - if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { - Transform2D gt = get_global_transform(); - gt.elements[2] -= collision.travel; - set_global_transform(gt); - return Vector2(); - } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling - on_ceiling = true; - } else { + if (p_floor_direction == Vector2()) { + //all is a wall on_wall = true; + } else { + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor + + on_floor = true; + on_floor_body = collision.collider_rid; + floor_velocity = collision.collider_vel; + + Vector2 rel_v = lv - floor_velocity; + Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + + if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); + return Vector2(); + } + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + } } - } - Vector2 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); + Vector2 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); - colliders.push_back(collision); + colliders.push_back(collision); + } + } - } else { + if (!found_collision) { break; } - p_max_slides--; if (motion == Vector2()) break; @@ -1077,6 +1301,31 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const return lv; } +Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { + + bool was_on_floor = on_floor; + + Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_slope_stop_min_velocity, p_max_slides, p_floor_max_angle); + if (!was_on_floor || p_snap == Vector2()) { + return ret; + } + + Collision col; + Transform2D gt = get_global_transform(); + + if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) { + gt.elements[2] += col.travel; + if (p_floor_direction != Vector2() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) { + on_floor = true; + on_floor_body = col.collider_rid; + floor_velocity = col.collider_vel; + } + set_global_transform(gt); + } + + return ret; +} + bool KinematicBody2D::is_on_floor() const { return on_floor; @@ -1130,18 +1379,68 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders[p_bounce].instance(); - slide_colliders[p_bounce]->owner = this; + slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce]->owner = this; } - slide_colliders[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; return slide_colliders[p_bounce]; } +void KinematicBody2D::set_sync_to_physics(bool p_enable) { + + if (sync_to_physics == p_enable) { + return; + } + sync_to_physics = p_enable; + if (p_enable) { + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, ""); + set_only_update_transform_changes(false); + set_notify_local_transform(false); + } +} + +bool KinematicBody2D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void KinematicBody2D::_direct_state_changed(Object *p_state) { + + if (!sync_to_physics) + return; + + Physics2DDirectBodyState *state = Object::cast_to<Physics2DDirectBodyState>(p_state); + + last_valid_transform = state->get_transform(); + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); +} + +void KinematicBody2D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + last_valid_transform = get_global_transform(); + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + //used by sync to physics, send the new transform to the physics + Transform2D new_transform = get_global_transform(); + Physics2DServer::get_singleton()->body_set_state(get_rid(), Physics2DServer::BODY_STATE_TRANSFORM, new_transform); + //but then revert changes + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + } +} void KinematicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody2D::_move, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); @@ -1156,7 +1455,13 @@ void KinematicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody2D::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody2D::_get_slide_collision); + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &KinematicBody2D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &KinematicBody2D::is_sync_to_physics_enabled); + + ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody2D::_direct_state_changed); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "motion/sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } KinematicBody2D::KinematicBody2D() : @@ -1167,6 +1472,7 @@ KinematicBody2D::KinematicBody2D() : on_floor = false; on_ceiling = false; on_wall = false; + sync_to_physics = false; } KinematicBody2D::~KinematicBody2D() { if (motion_cache.is_valid()) { @@ -1175,7 +1481,7 @@ KinematicBody2D::~KinematicBody2D() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { - slide_colliders[i]->owner = NULL; + slide_colliders.write[i]->owner = NULL; } } } diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 0fda3c5c05..0a2ce0918b 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -32,6 +32,7 @@ #define PHYSICS_BODY_2D_H #include "scene/2d/collision_object_2d.h" +#include "scene/resources/physics_material.h" #include "servers/physics_2d_server.h" #include "vset.h" @@ -79,18 +80,21 @@ class StaticBody2D : public PhysicsBody2D { Vector2 constant_linear_velocity; real_t constant_angular_velocity; - real_t bounce; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; protected: static void _bind_methods(); public: +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_constant_linear_velocity(const Vector2 &p_vel); void set_constant_angular_velocity(real_t p_vel); @@ -100,6 +104,9 @@ public: StaticBody2D(); ~StaticBody2D(); + +private: + void _reload_physics_characteristics(); }; class RigidBody2D : public PhysicsBody2D { @@ -125,9 +132,8 @@ private: Physics2DDirectBodyState *state; Mode mode; - real_t bounce; real_t mass; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; real_t gravity_scale; real_t linear_damp; real_t angular_damp; @@ -204,11 +210,16 @@ public: void set_weight(real_t p_weight); real_t get_weight() const; +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; @@ -245,7 +256,9 @@ public: void set_continuous_collision_detection_mode(CCDMode p_mode); CCDMode get_continuous_collision_detection_mode() const; + void apply_central_impulse(const Vector2 &p_impulse); void apply_impulse(const Vector2 &p_offset, const Vector2 &p_impulse); + void apply_torque_impulse(float p_torque); void set_applied_force(const Vector2 &p_force); Vector2 get_applied_force() const; @@ -253,7 +266,9 @@ public: void set_applied_torque(const float p_torque); float get_applied_torque() const; + void add_central_force(const Vector2 &p_force); void add_force(const Vector2 &p_offset, const Vector2 &p_force); + void add_torque(float p_torque); Array get_colliding_bodies() const; //function for script @@ -261,6 +276,9 @@ public: RigidBody2D(); ~RigidBody2D(); + +private: + void _reload_physics_characteristics(); }; VARIANT_ENUM_CAST(RigidBody2D::Mode); @@ -276,6 +294,7 @@ public: Vector2 normal; Vector2 collider_vel; ObjectID collider; + RID collider_rid; int collider_shape; Variant collider_metadata; Vector2 remainder; @@ -287,29 +306,40 @@ private: float margin; Vector2 floor_velocity; + RID on_floor_body; bool on_floor; bool on_ceiling; bool on_wall; + bool sync_to_physics; + Vector<Collision> colliders; Vector<Ref<KinematicCollision2D> > slide_colliders; Ref<KinematicCollision2D> motion_cache; _FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const; - Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true); + Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); + Transform2D last_valid_transform; + void _direct_state_changed(Object *p_state); + protected: + void _notification(int p_what); static void _bind_methods(); public: - bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false); + bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia); + bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); + 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), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; @@ -318,6 +348,9 @@ public: int get_slide_count() const; Collision get_slide_collision(int p_bounce) const; + void set_sync_to_physics(bool p_enable); + bool is_sync_to_physics_enabled() const; + KinematicBody2D(); ~KinematicBody2D(); }; diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index 81ed3c63c3..c9e5408f06 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -113,7 +113,7 @@ void Polygon2D::_notification(int p_what) { PoolVector<Vector2>::Read polyr = polygon.read(); for (int i = 0; i < len; i++) { - points[i] = polyr[i] + offset; + points.write[i] = polyr[i] + offset; } } @@ -153,18 +153,18 @@ void Polygon2D::_notification(int p_what) { SWAP(ep[1], ep[4]); SWAP(ep[2], ep[3]); SWAP(ep[5], ep[0]); - SWAP(ep[6], points[highest_idx]); + SWAP(ep[6], points.write[highest_idx]); } points.resize(points.size() + 7); for (int i = points.size() - 1; i >= highest_idx + 7; i--) { - points[i] = points[i - 7]; + points.write[i] = points[i - 7]; } for (int i = 0; i < 7; i++) { - points[highest_idx + i + 1] = ep[i]; + points.write[highest_idx + i + 1] = ep[i]; } len = points.size(); @@ -182,12 +182,12 @@ void Polygon2D::_notification(int p_what) { PoolVector<Vector2>::Read uvr = uv.read(); for (int i = 0; i < len; i++) { - uvs[i] = texmat.xform(uvr[i]) / tex_size; + uvs.write[i] = texmat.xform(uvr[i]) / tex_size; } } else { for (int i = 0; i < len; i++) { - uvs[i] = texmat.xform(points[i]) / tex_size; + uvs.write[i] = texmat.xform(points[i]) / tex_size; } } } @@ -262,10 +262,10 @@ void Polygon2D::_notification(int p_what) { { PoolVector<Color>::Read color_r = vertex_colors.read(); for (int i = 0; i < color_len && i < len; i++) { - colors[i] = color_r[i]; + colors.write[i] = color_r[i]; } for (int i = color_len; i < len; i++) { - colors[i] = color; + colors.write[i] = color; } } @@ -333,13 +333,13 @@ void Polygon2D::_notification(int p_what) { Vector<Vector2> vertices; vertices.resize(loop.size()); for (int j = 0; j < vertices.size(); j++) { - vertices[j] = points[loop[j]]; + vertices.write[j] = points[loop[j]]; } Vector<int> sub_indices = Geometry::triangulate_polygon(vertices); int from = indices.size(); indices.resize(from + sub_indices.size()); for (int j = 0; j < sub_indices.size(); j++) { - indices[from + j] = loop[sub_indices[j]]; + indices.write[from + j] = loop[sub_indices[j]]; } } @@ -538,12 +538,12 @@ void Polygon2D::clear_bones() { void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) { ERR_FAIL_INDEX(p_index, bone_weights.size()); - bone_weights[p_index].weights = p_weights; + bone_weights.write[p_index].weights = p_weights; update(); } void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { ERR_FAIL_INDEX(p_index, bone_weights.size()); - bone_weights[p_index].path = p_path; + bone_weights.write[p_index].path = p_path; update(); } diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 8ceffb3c27..e6e9bde20a 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -191,13 +191,13 @@ void Skeleton2D::_update_bone_setup() { bones.sort(); //sorty so they are always in the same order/index for (int i = 0; i < bones.size(); i++) { - bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose - bones[i].bone->skeleton_index = i; + bones.write[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose + bones.write[i].bone->skeleton_index = i; Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent()); if (parent_bone) { - bones[i].parent_index = parent_bone->skeleton_index; + bones.write[i].parent_index = parent_bone->skeleton_index; } else { - bones[i].parent_index = -1; + bones.write[i].parent_index = -1; } } @@ -230,9 +230,9 @@ void Skeleton2D::_update_transform() { ERR_CONTINUE(bones[i].parent_index >= i); if (bones[i].parent_index >= 0) { - bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); + bones.write[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform(); } else { - bones[i].accum_transform = bones[i].bone->get_transform(); + bones.write[i].accum_transform = bones[i].bone->get_transform(); } } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 9a343ca0f0..80565fa455 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -62,7 +62,7 @@ void TileMap::_notification(int p_what) { pending_update = true; _recreate_quadrants(); - _update_dirty_quadrants(); + update_dirty_quadrants(); RID space = get_world_2d()->get_space(); _update_quadrant_transform(); _update_quadrant_space(space); @@ -245,7 +245,7 @@ void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const xform.elements[2].y += offset.y; } -void TileMap::_update_dirty_quadrants() { +void TileMap::update_dirty_quadrants() { if (!pending_update) return; @@ -368,7 +368,7 @@ void TileMap::_update_dirty_quadrants() { } Rect2 r = tile_set->tile_get_region(c.id); - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { int spacing = tile_set->autotile_get_spacing(c.id); r.size = tile_set->autotile_get_size(c.id); r.position += (r.size + Vector2(spacing, spacing)) * Vector2(c.autotile_coord_x, c.autotile_coord_y); @@ -491,7 +491,7 @@ void TileMap::_update_dirty_quadrants() { if (navigation) { Ref<NavigationPolygon> navpoly; Vector2 npoly_ofs; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { navpoly = tile_set->autotile_get_navigation_polygon(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); npoly_ofs = Vector2(); } else { @@ -529,8 +529,8 @@ void TileMap::_update_dirty_quadrants() { { PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read(); for (int i = 0; i < vsize; i++) { - vertices[i] = vr[i]; - colors[i] = debug_navigation_color; + vertices.write[i] = vr[i]; + colors.write[i] = debug_navigation_color; } } @@ -563,7 +563,7 @@ void TileMap::_update_dirty_quadrants() { } Ref<OccluderPolygon2D> occluder; - if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(c.id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(c.id) == TileSet::ATLAS_TILE) { occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y)); } else { occluder = tile_set->tile_get_light_occluder(c.id); @@ -631,11 +631,7 @@ void TileMap::_recompute_rect_cache() { r_total = r_total.merge(r); } - if (r_total == Rect2()) { - rect_cache = Rect2(-10, -10, 20, 20); - } else { - rect_cache = r_total.grow(MAX(cell_size.x, cell_size.y) * _get_quadrant_size()); - } + rect_cache = r_total; item_rect_changed(); @@ -721,7 +717,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool updat return; if (update) { - _update_dirty_quadrants(); + call_deferred("update_dirty_quadrants"); } } @@ -840,7 +836,7 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) { Map<PosKey, Cell>::Element *E = tile_map.find(p); if (E != NULL) { int id = get_cell(p_x, p_y); - if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE) { + if (tile_set->tile_get_tile_mode(id) == TileSet::AUTO_TILE || tile_set->tile_get_tile_mode(id) == TileSet::ATLAS_TILE) { uint16_t mask = 0; if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_2X2) { if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) { @@ -1026,7 +1022,7 @@ void TileMap::_recreate_quadrants() { Q->get().cells.insert(E->key()); _make_quadrant_dirty(Q, false); } - _update_dirty_quadrants(); + update_dirty_quadrants(); } void TileMap::_clear_quadrants() { @@ -1152,6 +1148,11 @@ PoolVector<int> TileMap::_get_tile_data() const { return data; } +Rect2 TileMap::_edit_get_rect() const { + const_cast<TileMap *>(this)->update_dirty_quadrants(); + return rect_cache; +} + void TileMap::set_collision_layer(uint32_t p_layer) { collision_layer = p_layer; @@ -1611,7 +1612,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cell", "x", "y", "tile", "flip_x", "flip_y", "transpose", "autotile_coord"), &TileMap::set_cell, DEFVAL(false), DEFVAL(false), DEFVAL(false), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("set_cellv", "position", "tile", "flip_x", "flip_y", "transpose"), &TileMap::set_cellv, DEFVAL(false), DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("set_celld", "data"), &TileMap::set_celld); + ClassDB::bind_method(D_METHOD("set_celld", "position", "data"), &TileMap::set_celld); ClassDB::bind_method(D_METHOD("get_cell", "x", "y"), &TileMap::get_cell); ClassDB::bind_method(D_METHOD("get_cellv", "position"), &TileMap::get_cellv); ClassDB::bind_method(D_METHOD("is_cell_x_flipped", "x", "y"), &TileMap::is_cell_x_flipped); @@ -1630,7 +1631,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_clear_quadrants"), &TileMap::_clear_quadrants); ClassDB::bind_method(D_METHOD("_recreate_quadrants"), &TileMap::_recreate_quadrants); - ClassDB::bind_method(D_METHOD("_update_dirty_quadrants"), &TileMap::_update_dirty_quadrants); + ClassDB::bind_method(D_METHOD("update_dirty_quadrants"), &TileMap::update_dirty_quadrants); ClassDB::bind_method(D_METHOD("update_bitmask_area", "position"), &TileMap::update_bitmask_area); ClassDB::bind_method(D_METHOD("update_bitmask_region", "start", "end"), &TileMap::update_bitmask_region, DEFVAL(Vector2()), DEFVAL(Vector2())); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 79d79ca59f..52aa6e8e2a 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -191,7 +191,6 @@ private: void _make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool update = true); void _recreate_quadrants(); void _clear_quadrants(); - void _update_dirty_quadrants(); void _update_quadrant_space(const RID &p_space); void _update_quadrant_transform(); void _recompute_rect_cache(); @@ -224,6 +223,8 @@ public: INVALID_CELL = -1 }; + virtual Rect2 _edit_get_rect() const; + void set_tileset(const Ref<TileSet> &p_tileset); Ref<TileSet> get_tileset() const; @@ -251,6 +252,8 @@ public: void update_cell_bitmask(int p_x, int p_y); void update_dirty_bitmask(); + void update_dirty_quadrants(); + void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 35c52a26c9..5f0ac3dd80 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -36,7 +36,7 @@ void AudioStreamPlayer3D::_mix_audio() { if (!stream_playback.is_valid() || !active || - (stream_paused && stream_paused_fade <= 0.f)) { + (stream_paused && !stream_paused_fade_out)) { return; } @@ -51,9 +51,13 @@ void AudioStreamPlayer3D::_mix_audio() { AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); + if (stream_paused_fade_out) { + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); + } + // Mix if we're not paused or we're fading out - if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX) && - (!stream_paused || stream_paused_fade > 0.f)) { + if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { float output_pitch_scale = 0.0; if (output_count) { @@ -103,15 +107,10 @@ void AudioStreamPlayer3D::_mix_audio() { int buffers = AudioServer::get_singleton()->get_channel_count(); for (int k = 0; k < buffers; k++) { - AudioFrame vol_inc = (current.vol[k] - prev_outputs[i].vol[k]) / float(buffer_size); - AudioFrame vol = current.vol[k]; - - if (stream_paused) { - vol = vol * stream_paused_fade; - if (stream_paused_fade > 0.f) { - stream_paused_fade -= 0.1f; - } - } + AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k]; + AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k]; + AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); + AudioFrame vol = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : current.vol[k]; AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); @@ -193,6 +192,8 @@ void AudioStreamPlayer3D::_mix_audio() { } output_ready = false; + stream_paused_fade_in = false; + stream_paused_fade_out = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { @@ -846,7 +847,8 @@ void AudioStreamPlayer3D::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_paused_fade = stream_paused ? 1.f : 0.f; + stream_paused_fade_in = stream_paused ? false : true; + stream_paused_fade_out = stream_paused ? true : false; } } @@ -984,10 +986,12 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { out_of_range_mode = OUT_OF_RANGE_MIX; doppler_tracking = DOPPLER_TRACKING_DISABLED; stream_paused = false; - stream_paused_fade = 0.f; + stream_paused_fade_in = false; + stream_paused_fade_out = false; velocity_tracker.instance(); AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); + set_disable_scale(true); } AudioStreamPlayer3D::~AudioStreamPlayer3D() { } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index cab1ff121a..14413d0702 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -107,9 +107,10 @@ private: float unit_size; float max_db; float pitch_scale; - float stream_paused_fade; bool autoplay; bool stream_paused; + bool stream_paused_fade_in; + bool stream_paused_fade_out; StringName bus; void _mix_audio(); diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp index 204aaef7ec..26fd5ed658 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/baked_lightmap.cpp @@ -811,4 +811,5 @@ BakedLightmap::BakedLightmap() { propagation = 1; hdr = false; image_path = "."; + set_disable_scale(true); } diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index e11e8abe5b..2176b45faf 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -74,10 +74,7 @@ void Camera::_update_camera() { if (!is_inside_tree()) return; - Transform tr = get_camera_transform(); - tr.origin += tr.basis.get_axis(1) * v_offset; - tr.origin += tr.basis.get_axis(0) * h_offset; - VisualServer::get_singleton()->camera_set_transform(camera, tr); + VisualServer::get_singleton()->camera_set_transform(camera, get_camera_transform()); // here goes listener stuff /* @@ -143,7 +140,10 @@ void Camera::_notification(int p_what) { Transform Camera::get_camera_transform() const { - return get_global_transform().orthonormalized(); + Transform tr = get_global_transform().orthonormalized(); + tr.origin += tr.basis.get_axis(1) * v_offset; + tr.origin += tr.basis.get_axis(0) * h_offset; + return tr; } void Camera::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { @@ -468,6 +468,10 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking); + + ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera::set_cull_mask_bit); + ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera::get_cull_mask_bit); + //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); @@ -550,6 +554,20 @@ uint32_t Camera::get_cull_mask() const { return layers; } +void Camera::set_cull_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_cull_mask(layers | (1 << p_layer)); + } else { + set_cull_mask(layers & (~(1 << p_layer))); + } +} + +bool Camera::get_cull_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + Vector<Plane> Camera::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); @@ -613,6 +631,7 @@ Camera::Camera() { velocity_tracker.instance(); doppler_tracking = DOPPLER_TRACKING_DISABLED; set_notify_transform(true); + set_disable_scale(true); } Camera::~Camera() { diff --git a/scene/3d/camera.h b/scene/3d/camera.h index 1b506e0c4f..97705d8ae0 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -142,6 +142,9 @@ public: void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; + void set_cull_mask_bit(int p_layer, bool p_enable); + bool get_cull_mask_bit(int p_layer) const; + virtual Vector<Plane> get_frustum() const; void set_environment(const Ref<Environment> &p_environment); diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 1d5d1b2afe..e19e45b263 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -305,7 +305,7 @@ void CollisionObject::shape_owner_remove_shape(uint32_t p_owner, int 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; + E->get().shapes.write[i].index -= 1; } } } diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index f31d65e411..f8ef04b78f 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -39,6 +39,7 @@ class CollisionObject : public Spatial { GDCLASS(CollisionObject, Spatial); bool area; + RID rid; struct ShapeData { diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index c9c91a5824..6ca8e80ea1 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -49,6 +49,7 @@ class CollisionShape : public Spatial { void resource_changed(RES res); bool disabled; +protected: void _create_debug_shape(); void _update_in_shape_owner(bool p_xform_only = false); diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp index 2e897c1c73..8b2000d2e9 100644 --- a/scene/3d/cpu_particles.cpp +++ b/scene/3d/cpu_particles.cpp @@ -25,6 +25,8 @@ void CPUParticles::set_emitting(bool p_emitting) { update_mutex->lock(); #endif VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); + #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -446,6 +448,8 @@ float rand_from_seed_m1_p1(uint32_t &seed) { void CPUParticles::_particles_process(float p_delta) { + p_delta *= speed_scale; + int pcount = particles.size(); PoolVector<Particle>::Write w = particles.write(); @@ -475,7 +479,7 @@ void CPUParticles::_particles_process(float p_delta) { if (!emitting && !p.active) continue; - float restart_time = float(i) / float(pcount); + float restart_time = (float(i) / float(pcount)) * lifetime; float local_delta = p_delta; if (randomness_ratio > 0.0) { @@ -643,7 +647,7 @@ void CPUParticles::_particles_process(float p_delta) { uint32_t alt_seed = p.seed; p.time += local_delta; - p.custom[1] += p.time / lifetime; + p.custom[1] = p.time / lifetime; float tex_linear_velocity = 0.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { @@ -979,6 +983,7 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->connect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true); #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -992,6 +997,7 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); #ifndef NO_THREADS update_mutex->unlock(); #endif @@ -1018,6 +1024,8 @@ void CPUParticles::_notification(int p_what) { update_mutex->lock(); #endif VS::get_singleton()->disconnect("frame_pre_draw", this, "_update_render_thread"); + VS::get_singleton()->instance_geometry_set_flag(get_instance(), VS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, false); + #ifndef NO_THREADS update_mutex->unlock(); #endif diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index 4ad2eb60ee..6276d02eff 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -557,6 +557,7 @@ GIProbe::GIProbe() { compress = false; gi_probe = VS::get_singleton()->gi_probe_create(); + set_disable_scale(true); } GIProbe::~GIProbe() { diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 7c42638107..16164cf3bf 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -306,6 +306,7 @@ Light::Light(VisualServer::LightType p_type) { set_param(PARAM_SHADOW_SPLIT_3_OFFSET, 0.5); set_param(PARAM_SHADOW_NORMAL_BIAS, 0.0); set_param(PARAM_SHADOW_BIAS, 0.15); + set_disable_scale(true); } Light::Light() { diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index e836a6154a..e277cae5b7 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -36,6 +36,7 @@ #include "scene/resources/material.h" #include "scene/scene_string_names.h" #include "skeleton.h" + bool MeshInstance::_set(const StringName &p_name, const Variant &p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -256,7 +257,7 @@ void MeshInstance::set_surface_material(int p_surface, const Ref<Material> &p_ma ERR_FAIL_INDEX(p_surface, materials.size()); - materials[p_surface] = p_material; + materials.write[p_surface] = p_material; if (materials[p_surface].is_valid()) VS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, materials[p_surface]->get_rid()); diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 5d359cd4d5..0dfec538f9 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -41,6 +41,7 @@ class MeshInstance : public GeometryInstance { GDCLASS(MeshInstance, GeometryInstance); +protected: Ref<Mesh> mesh; NodePath skeleton_path; diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp index 77bf703706..f5b77d361c 100644 --- a/scene/3d/navigation.cpp +++ b/scene/3d/navigation.cpp @@ -73,7 +73,7 @@ void Navigation::_navmesh_link(int p_id) { Vector3 ep = nm.xform.xform(r[idx]); center += ep; e.point = _get_point(ep); - p.edges[j] = e; + p.edges.write[j] = e; if (j >= 2) { Vector3 epa = nm.xform.xform(r[indices[j - 2]]); @@ -118,7 +118,7 @@ void Navigation::_navmesh_link(int p_id) { ConnectionPending pending; pending.polygon = &p; pending.edge = j; - p.edges[j].P = C->get().pending.push_back(pending); + p.edges.write[j].P = C->get().pending.push_back(pending); continue; //print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b)); } @@ -126,10 +126,10 @@ void Navigation::_navmesh_link(int p_id) { C->get().B = &p; C->get().B_edge = j; - C->get().A->edges[C->get().A_edge].C = &p; - C->get().A->edges[C->get().A_edge].C_edge = j; - p.edges[j].C = C->get().A; - p.edges[j].C_edge = C->get().A_edge; + C->get().A->edges.write[C->get().A_edge].C = &p; + C->get().A->edges.write[C->get().A_edge].C_edge = j; + p.edges.write[j].C = C->get().A; + p.edges.write[j].C_edge = C->get().A_edge; //connection successful. } } @@ -165,10 +165,10 @@ void Navigation::_navmesh_unlink(int p_id) { } else if (C->get().B) { //disconnect - C->get().B->edges[C->get().B_edge].C = NULL; - C->get().B->edges[C->get().B_edge].C_edge = -1; - C->get().A->edges[C->get().A_edge].C = NULL; - C->get().A->edges[C->get().A_edge].C_edge = -1; + C->get().B->edges.write[C->get().B_edge].C = NULL; + C->get().B->edges.write[C->get().B_edge].C_edge = -1; + C->get().A->edges.write[C->get().A_edge].C = NULL; + C->get().A->edges.write[C->get().A_edge].C_edge = -1; if (C->get().A == &E->get()) { @@ -185,11 +185,11 @@ void Navigation::_navmesh_unlink(int p_id) { C->get().B = cp.polygon; C->get().B_edge = cp.edge; - C->get().A->edges[C->get().A_edge].C = cp.polygon; - C->get().A->edges[C->get().A_edge].C_edge = cp.edge; - cp.polygon->edges[cp.edge].C = C->get().A; - cp.polygon->edges[cp.edge].C_edge = C->get().A_edge; - cp.polygon->edges[cp.edge].P = NULL; + C->get().A->edges.write[C->get().A_edge].C = cp.polygon; + C->get().A->edges.write[C->get().A_edge].C_edge = cp.edge; + cp.polygon->edges.write[cp.edge].C = C->get().A; + cp.polygon->edges.write[cp.edge].C_edge = C->get().A_edge; + cp.polygon->edges.write[cp.edge].P = NULL; } } else { @@ -320,8 +320,8 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector Vector<Vector3> path; path.resize(2); - path[0] = begin_point; - path[1] = end_point; + path.write[0] = begin_point; + path.write[1] = end_point; //print_line("Direct Path"); return path; } @@ -375,7 +375,7 @@ Vector<Vector3> Navigation::get_simple_path(const Vector3 &p_start, const Vector for (int i = 0; i < p->edges.size(); i++) { - Polygon::Edge &e = p->edges[i]; + Polygon::Edge &e = p->edges.write[i]; if (!e.C) continue; diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index 073e56fdb4..99680b7273 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -55,9 +55,9 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) { for (int j = 0; j < rlen; j += 3) { Vector<int> vi; vi.resize(3); - vi[0] = r[j + 0] + from; - vi[1] = r[j + 1] + from; - vi[2] = r[j + 2] + from; + vi.write[0] = r[j + 0] + from; + vi.write[1] = r[j + 1] + from; + vi.write[2] = r[j + 2] + from; add_polygon(vi); } @@ -215,7 +215,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) { polygons.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { - polygons[i].indices = p_array[i]; + polygons.write[i].indices = p_array[i]; } } diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 2b3a62fcdc..4900692155 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -185,7 +185,7 @@ void Particles::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); - draw_passes[p_pass] = p_mesh; + draw_passes.write[p_pass] = p_mesh; RID mesh_rid; if (p_mesh.is_valid()) diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index e851c8d643..84a0fb9b1d 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -30,6 +30,7 @@ #include "physics_body.h" +#include "core/core_string_names.h" #include "engine.h" #include "method_bind_ext.gen.inc" #include "scene/scene_string_names.h" @@ -121,23 +122,23 @@ bool PhysicsBody::get_collision_layer_bit(int p_bit) const { void PhysicsBody::add_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_add_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::remove_collision_exception_with(Node *p_node) { ERR_FAIL_NULL(p_node); - PhysicsBody *physics_body = Object::cast_to<PhysicsBody>(p_node); - if (!physics_body) { - ERR_EXPLAIN("Collision exception only works between two objects of PhysicsBody type"); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); } - ERR_FAIL_COND(!physics_body); - PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), physics_body->get_rid()); + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } void PhysicsBody::_set_layers(uint32_t p_mask) { @@ -178,28 +179,77 @@ PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : collision_mask = 1; } +#ifndef DISABLE_DEPRECATED void StaticBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + + physics_material_override->set_friction(p_friction); } + real_t StaticBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void StaticBody::set_bounce(real_t p_bounce) { + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } + real_t StaticBody::get_bounce() const { - return bounce; + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + + if (physics_material_override.is_null()) { + return 0; + } + + return physics_material_override->get_bounce(); +} +#endif + +void StaticBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> StaticBody::get_physics_material_override() const { + return physics_material_override; } void StaticBody::set_constant_linear_velocity(const Vector3 &p_vel) { @@ -230,30 +280,45 @@ void StaticBody::_bind_methods() { ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody::get_constant_linear_velocity); ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody::get_constant_angular_velocity); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &StaticBody::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &StaticBody::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &StaticBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &StaticBody::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &StaticBody::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody::add_collision_exception_with); ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody::remove_collision_exception_with); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); - +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); } StaticBody::StaticBody() : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) { - - bounce = 0; - friction = 1; } -StaticBody::~StaticBody() { +StaticBody::~StaticBody() {} + +void StaticBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } } void RigidBody::_body_enter_tree(ObjectID p_id) { @@ -550,28 +615,68 @@ real_t RigidBody::get_weight() const { return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); } +#ifndef DISABLE_DEPRECATED void RigidBody::set_friction(real_t p_friction) { + ERR_EXPLAIN("The method set_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_friction < 0 || p_friction > 1); - friction = p_friction; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_friction(p_friction); } real_t RigidBody::get_friction() const { - return friction; + ERR_EXPLAIN("The method get_friction has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 1; + } + + return physics_material_override->get_friction(); } void RigidBody::set_bounce(real_t p_bounce) { - + ERR_EXPLAIN("The method set_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1); - bounce = p_bounce; - PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); + if (physics_material_override.is_null()) { + physics_material_override.instance(); + set_physics_material_override(physics_material_override); + } + physics_material_override->set_bounce(p_bounce); } real_t RigidBody::get_bounce() const { + ERR_EXPLAIN("The method get_bounce has been deprecated and will be removed in the future, use physical material") + WARN_DEPRECATED + if (physics_material_override.is_null()) { + return 0; + } - return bounce; + return physics_material_override->get_bounce(); +} +#endif // DISABLE_DEPRECATED + +void RigidBody::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { + if (physics_material_override.is_valid()) { + if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics")) + physics_material_override->disconnect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + + physics_material_override = p_physics_material_override; + + if (physics_material_override.is_valid()) { + physics_material_override->connect(CoreStringNames::get_singleton()->changed, this, "_reload_physics_characteristics"); + } + _reload_physics_characteristics(); +} + +Ref<PhysicsMaterial> RigidBody::get_physics_material_override() const { + return physics_material_override; } void RigidBody::set_gravity_scale(real_t p_gravity_scale) { @@ -693,6 +798,22 @@ int RigidBody::get_max_contacts_reported() const { return max_contacts_reported; } +void RigidBody::add_central_force(const Vector3 &p_force) { + PhysicsServer::get_singleton()->body_add_central_force(get_rid(), p_force); +} + +void RigidBody::add_force(const Vector3 &p_force, const Vector3 &p_pos) { + PhysicsServer::get_singleton()->body_add_force(get_rid(), p_force, p_pos); +} + +void RigidBody::add_torque(const Vector3 &p_torque) { + PhysicsServer::get_singleton()->body_add_torque(get_rid(), p_torque); +} + +void RigidBody::apply_central_impulse(const Vector3 &p_impulse) { + PhysicsServer::get_singleton()->body_apply_central_impulse(get_rid(), p_impulse); +} + void RigidBody::apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse) { PhysicsServer::get_singleton()->body_apply_impulse(get_rid(), p_pos, p_impulse); @@ -806,11 +927,18 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody::set_weight); ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody::get_weight); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_friction", "friction"), &RigidBody::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &RigidBody::get_friction); ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &RigidBody::set_bounce); ClassDB::bind_method(D_METHOD("get_bounce"), &RigidBody::get_bounce); +#endif // DISABLE_DEPRECATED + + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody::set_physics_material_override); + ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody::get_physics_material_override); + + ClassDB::bind_method(D_METHOD("_reload_physics_characteristics"), &RigidBody::_reload_physics_characteristics); ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &RigidBody::set_linear_velocity); ClassDB::bind_method(D_METHOD("get_linear_velocity"), &RigidBody::get_linear_velocity); @@ -840,6 +968,12 @@ void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("is_using_continuous_collision_detection"), &RigidBody::is_using_continuous_collision_detection); ClassDB::bind_method(D_METHOD("set_axis_velocity", "axis_velocity"), &RigidBody::set_axis_velocity); + + ClassDB::bind_method(D_METHOD("add_central_force", "force"), &RigidBody::add_central_force); + ClassDB::bind_method(D_METHOD("add_force", "force", "position"), &RigidBody::add_force); + ClassDB::bind_method(D_METHOD("add_torque", "torque"), &RigidBody::add_torque); + + ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &RigidBody::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "position", "impulse"), &RigidBody::apply_impulse); ClassDB::bind_method(D_METHOD("apply_torque_impulse", "impulse"), &RigidBody::apply_torque_impulse); @@ -863,8 +997,11 @@ void RigidBody::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); +#ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); +#endif // DISABLE_DEPRECATED + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "continuous_cd"), "set_use_continuous_collision_detection", "is_using_continuous_collision_detection"); @@ -903,9 +1040,7 @@ RigidBody::RigidBody() : mode = MODE_RIGID; - bounce = 0; mass = 1; - friction = 1; max_contacts_reported = 0; state = NULL; @@ -929,6 +1064,17 @@ RigidBody::~RigidBody() { if (contact_monitor) memdelete(contact_monitor); } + +void RigidBody::_reload_physics_characteristics() { + if (physics_material_override.is_null()) { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, 0); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, 1); + } else { + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, physics_material_override->computed_bounce()); + PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, physics_material_override->computed_friction()); + } +} + ////////////////////////////////////////////////////// ////////////////////////// @@ -979,6 +1125,9 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in return colliding; } +//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +#define FLOOR_ANGLE_THRESHOLD 0.01 + Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { Vector3 lv = p_linear_velocity; @@ -1011,7 +1160,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve //all is a wall on_wall = true; } else { - if (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_ANGLE_THRESHOLD)) { //floor on_floor = true; floor_velocity = collision.collider_vel; @@ -1025,7 +1174,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve set_global_transform(gt); return floor_velocity - p_floor_direction * p_floor_direction.dot(floor_velocity); } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling on_ceiling = true; } else { on_wall = true; @@ -1117,11 +1266,11 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { } if (slide_colliders[p_bounce].is_null()) { - slide_colliders[p_bounce].instance(); - slide_colliders[p_bounce]->owner = this; + slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce]->owner = this; } - slide_colliders[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; return slide_colliders[p_bounce]; } @@ -1174,7 +1323,7 @@ KinematicBody::~KinematicBody() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { - slide_colliders[i]->owner = NULL; + slide_colliders.write[i]->owner = NULL; } } } @@ -2228,6 +2377,7 @@ void PhysicalBone::set_bounce(real_t p_bounce) { bounce = p_bounce; PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce); } + real_t PhysicalBone::get_bounce() const { return bounce; diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 0190dcbfc3..80bf422c98 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -32,6 +32,7 @@ #define PHYSICS_BODY__H #include "scene/3d/collision_object.h" +#include "scene/resources/physics_material.h" #include "servers/physics_server.h" #include "skeleton.h" #include "vset.h" @@ -81,18 +82,22 @@ class StaticBody : public PhysicsBody { Vector3 constant_linear_velocity; Vector3 constant_angular_velocity; - real_t bounce; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; protected: static void _bind_methods(); public: +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_constant_linear_velocity(const Vector3 &p_vel); void set_constant_angular_velocity(const Vector3 &p_vel); @@ -102,6 +107,9 @@ public: StaticBody(); ~StaticBody(); + +private: + void _reload_physics_characteristics(); }; class RigidBody : public PhysicsBody { @@ -121,9 +129,8 @@ protected: PhysicsDirectBodyState *state; Mode mode; - real_t bounce; real_t mass; - real_t friction; + Ref<PhysicsMaterial> physics_material_override; Vector3 linear_velocity; Vector3 angular_velocity; @@ -196,11 +203,16 @@ public: void set_weight(real_t p_weight); real_t get_weight() const; +#ifndef DISABLE_DEPRECATED void set_friction(real_t p_friction); real_t get_friction() const; void set_bounce(real_t p_bounce); real_t get_bounce() const; +#endif + + void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); + Ref<PhysicsMaterial> get_physics_material_override() const; void set_linear_velocity(const Vector3 &p_velocity); Vector3 get_linear_velocity() const; @@ -242,6 +254,11 @@ public: Array get_colliding_bodies() const; + void add_central_force(const Vector3 &p_force); + void add_force(const Vector3 &p_force, const Vector3 &p_pos); + void add_torque(const Vector3 &p_torque); + + void apply_central_impulse(const Vector3 &p_impulse); void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); void apply_torque_impulse(const Vector3 &p_impulse); @@ -249,6 +266,9 @@ public: RigidBody(); ~RigidBody(); + +private: + void _reload_physics_characteristics(); }; VARIANT_ENUM_CAST(RigidBody::Mode); @@ -294,7 +314,7 @@ protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz); bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); @@ -537,6 +557,7 @@ protected: private: static Skeleton *find_skeleton_parent(Node *p_parent); + void _fix_joint_offset(); void _reload_joint(); diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index 4d50945062..fe522bbe97 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -274,6 +274,7 @@ ReflectionProbe::ReflectionProbe() { probe = VisualServer::get_singleton()->reflection_probe_create(); VS::get_singleton()->instance_set_base(get_instance(), probe); + set_disable_scale(true); } ReflectionProbe::~ReflectionProbe() { diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 8d91b6f09f..c796e47f25 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -66,7 +66,7 @@ bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { Array children = p_value; if (is_inside_tree()) { - bones[which].nodes_bound.clear(); + bones.write[which].nodes_bound.clear(); for (int i = 0; i < children.size(); i++) { @@ -131,7 +131,7 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const { String prep = "bones/" + itos(i) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, prep + "name")); - p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(i - 1) + ",1")); + p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1")); p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest")); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled")); p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); @@ -139,6 +139,59 @@ void Skeleton::_get_property_list(List<PropertyInfo> *p_list) const { } } +void Skeleton::_update_process_order() { + + if (!process_order_dirty) + return; + + Bone *bonesptr = bones.ptrw(); + int len = bones.size(); + + process_order.resize(len); + int *order = process_order.ptrw(); + for (int i = 0; i < len; i++) { + + if (bonesptr[i].parent >= len) { + //validate this just in case + ERR_PRINTS("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); + bonesptr[i].parent = -1; + } + order[i] = i; + bonesptr[i].sort_index = i; + } + //now check process order + int pass_count = 0; + while (pass_count < len * len) { + //using bubblesort because of simplicity, it wont run every frame though. + //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic + bool swapped = false; + for (int i = 0; i < len; i++) { + int parent_idx = bonesptr[order[i]].parent; + if (parent_idx < 0) + continue; //do nothing because it has no parent + //swap indices + int parent_order = bonesptr[parent_idx].sort_index; + if (parent_order > i) { + bonesptr[order[i]].sort_index = parent_order; + bonesptr[parent_idx].sort_index = i; + //swap order + SWAP(order[i], order[parent_order]); + swapped = true; + } + } + + if (!swapped) + break; + pass_count++; + } + + if (pass_count == len * len) { + ERR_PRINT("Skeleton parenthood graph is cyclic"); + } + + process_order_dirty = false; +} + void Skeleton::_notification(int p_what) { switch (p_what) { @@ -176,24 +229,28 @@ void Skeleton::_notification(int p_what) { case NOTIFICATION_UPDATE_SKELETON: { VisualServer *vs = VisualServer::get_singleton(); - Bone *bonesptr = &bones[0]; + Bone *bonesptr = bones.ptrw(); int len = bones.size(); vs->skeleton_allocate(skeleton, len); // if same size, nothin really happens + _update_process_order(); + + const int *order = process_order.ptr(); + // pose changed, rebuild cache of inverses if (rest_global_inverse_dirty) { // calculate global rests and invert them for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.parent >= 0) b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest; else b.rest_global_inverse = b.rest; } for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; b.rest_global_inverse.affine_invert(); } @@ -205,7 +262,7 @@ void Skeleton::_notification(int p_what) { for (int i = 0; i < len; i++) { - Bone &b = bonesptr[i]; + Bone &b = bonesptr[order[i]]; if (b.disable_rest) { if (b.enabled) { @@ -319,12 +376,13 @@ void Skeleton::add_bone(const String &p_name) { for (int i = 0; i < bones.size(); i++) { - ERR_FAIL_COND(bones[i].name == "p_name"); + ERR_FAIL_COND(bones[i].name == p_name); } Bone b; b.name = p_name; bones.push_back(b); + process_order_dirty = true; rest_global_inverse_dirty = true; _make_dirty(); @@ -368,10 +426,11 @@ int Skeleton::get_bone_count() const { void Skeleton::set_bone_parent(int p_bone, int p_parent) { ERR_FAIL_INDEX(p_bone, bones.size()); - ERR_FAIL_COND(p_parent != -1 && (p_parent < 0 || p_parent >= p_bone)); + ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); - bones[p_bone].parent = p_parent; + bones.write[p_bone].parent = p_parent; rest_global_inverse_dirty = true; + process_order_dirty = true; _make_dirty(); } @@ -379,21 +438,24 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); + _update_process_order(); + int parent = bones[p_bone].parent; while (parent >= 0) { - bones[p_bone].rest = bones[parent].rest * bones[p_bone].rest; + bones.write[p_bone].rest = bones[parent].rest * bones[p_bone].rest; parent = bones[parent].parent; } - bones[p_bone].parent = -1; - bones[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing + bones.write[p_bone].parent = -1; + bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing + process_order_dirty = true; _make_dirty(); } void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].ignore_animation = p_ignore; + bones.write[p_bone].ignore_animation = p_ignore; } bool Skeleton::is_bone_ignore_animation(int p_bone) const { @@ -405,7 +467,7 @@ bool Skeleton::is_bone_ignore_animation(int p_bone) const { void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].disable_rest = p_disable; + bones.write[p_bone].disable_rest = p_disable; } bool Skeleton::is_bone_rest_disabled(int p_bone) const { @@ -425,7 +487,7 @@ void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].rest = p_rest; + bones.write[p_bone].rest = p_rest; rest_global_inverse_dirty = true; _make_dirty(); } @@ -440,7 +502,7 @@ void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].enabled = p_enabled; + bones.write[p_bone].enabled = p_enabled; rest_global_inverse_dirty = true; _make_dirty(); } @@ -457,13 +519,13 @@ void Skeleton::bind_child_node_to_bone(int p_bone, Node *p_node) { uint32_t id = p_node->get_instance_id(); - for (List<uint32_t>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { + for (const List<uint32_t>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { if (E->get() == id) return; // already here } - bones[p_bone].nodes_bound.push_back(id); + bones.write[p_bone].nodes_bound.push_back(id); } void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { @@ -471,7 +533,7 @@ void Skeleton::unbind_child_node_from_bone(int p_bone, Node *p_node) { ERR_FAIL_INDEX(p_bone, bones.size()); uint32_t id = p_node->get_instance_id(); - bones[p_bone].nodes_bound.erase(id); + bones.write[p_bone].nodes_bound.erase(id); } void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const { @@ -489,6 +551,8 @@ void Skeleton::clear_bones() { bones.clear(); rest_global_inverse_dirty = true; + process_order_dirty = true; + _make_dirty(); } @@ -499,7 +563,7 @@ void Skeleton::set_bone_pose(int p_bone, const Transform &p_pose) { ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(!is_inside_tree()); - bones[p_bone].pose = p_pose; + bones.write[p_bone].pose = p_pose; _make_dirty(); } Transform Skeleton::get_bone_pose(int p_bone) const { @@ -513,8 +577,8 @@ void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) ERR_FAIL_INDEX(p_bone, bones.size()); //ERR_FAIL_COND( !is_inside_scene() ); - bones[p_bone].custom_pose_enable = (p_custom_pose != Transform()); - bones[p_bone].custom_pose = p_custom_pose; + bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); + bones.write[p_bone].custom_pose = p_custom_pose; _make_dirty(); } @@ -538,12 +602,21 @@ void Skeleton::_make_dirty() { dirty = true; } +int Skeleton::get_process_order(int p_idx) { + ERR_FAIL_INDEX_V(p_idx, bones.size(), -1); + _update_process_order(); + return process_order[p_idx]; +} + void Skeleton::localize_rests() { - for (int i = bones.size() - 1; i >= 0; i--) { + _update_process_order(); - if (bones[i].parent >= 0) - set_bone_rest(i, bones[bones[i].parent].rest.affine_inverse() * bones[i].rest); + for (int i = bones.size() - 1; i >= 0; i--) { + int idx = process_order[i]; + if (bones[idx].parent >= 0) { + set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest); + } } } @@ -553,14 +626,14 @@ void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_b ERR_FAIL_INDEX(p_bone, bones.size()); ERR_FAIL_COND(bones[p_bone].physical_bone); ERR_FAIL_COND(!p_physical_bone); - bones[p_bone].physical_bone = p_physical_bone; + bones.write[p_bone].physical_bone = p_physical_bone; _rebuild_physical_bones_cache(); } void Skeleton::unbind_physical_bone_from_bone(int p_bone) { ERR_FAIL_INDEX(p_bone, bones.size()); - bones[p_bone].physical_bone = NULL; + bones.write[p_bone].physical_bone = NULL; _rebuild_physical_bones_cache(); } @@ -600,9 +673,12 @@ PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) { void Skeleton::_rebuild_physical_bones_cache() { const int b_size = bones.size(); for (int i = 0; i < b_size; ++i) { - bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i); - if (bones[i].physical_bone) - bones[i].physical_bone->_on_bone_parent_changed(); + PhysicalBone *parent_pb = _get_physical_bone_parent(i); + if (parent_pb != bones[i].physical_bone) { + bones.write[i].cache_parent_physical_bone = parent_pb; + if (bones[i].physical_bone) + bones[i].physical_bone->_on_bone_parent_changed(); + } } } @@ -660,7 +736,7 @@ void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) { if (Variant::STRING == p_bones.get(i).get_type()) { int bone_id = find_bone(p_bones.get(i)); if (bone_id != -1) - sim_bones[c++] = bone_id; + sim_bones.write[c++] = bone_id; } } sim_bones.resize(c); @@ -740,6 +816,8 @@ void Skeleton::_bind_methods() { #endif // _3D_DISABLED + ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } @@ -747,6 +825,7 @@ Skeleton::Skeleton() { rest_global_inverse_dirty = true; dirty = false; + process_order_dirty = true; skeleton = VisualServer::get_singleton()->skeleton_create(); set_notify_transform(true); } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 9672acb57a..e044e08437 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -39,6 +39,8 @@ */ #ifndef _3D_DISABLED +typedef int BoneId; + class PhysicalBone; #endif // _3D_DISABLED @@ -52,6 +54,7 @@ class Skeleton : public Spatial { bool enabled; int parent; + int sort_index; //used for re-sorting process order bool ignore_animation; @@ -90,13 +93,15 @@ class Skeleton : public Spatial { bool rest_global_inverse_dirty; Vector<Bone> bones; + Vector<int> process_order; + bool process_order_dirty; RID skeleton; void _make_dirty(); bool dirty; - //bind helpers + // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { Array bound; @@ -110,6 +115,8 @@ class Skeleton : public Spatial { return bound; } + void _update_process_order(); + protected: bool _get(const StringName &p_path, Variant &r_ret) const; bool _set(const StringName &p_path, const Variant &p_value); @@ -170,6 +177,7 @@ public: Transform get_bone_custom_pose(int p_bone) const; void localize_rests(); // used for loaders and tools + int get_process_order(int p_idx); #ifndef _3D_DISABLED // Physical bone API diff --git a/scene/3d/soft_body.cpp b/scene/3d/soft_body.cpp new file mode 100644 index 0000000000..980c348c9b --- /dev/null +++ b/scene/3d/soft_body.cpp @@ -0,0 +1,799 @@ +/*************************************************************************/ +/* soft_physics_body.cpp */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "soft_body.h" +#include "os/os.h" +#include "scene/3d/collision_object.h" +#include "scene/3d/skeleton.h" +#include "servers/physics_server.h" + +SoftBodyVisualServerHandler::SoftBodyVisualServerHandler() {} + +void SoftBodyVisualServerHandler::prepare(RID p_mesh, int p_surface) { + clear(); + + ERR_FAIL_COND(!p_mesh.is_valid()); + + mesh = p_mesh; + surface = p_surface; + + const uint32_t surface_format = VS::get_singleton()->mesh_surface_get_format(mesh, surface); + const int surface_vertex_len = VS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface); + const int surface_index_len = VS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface); + uint32_t surface_offsets[VS::ARRAY_MAX]; + + buffer = VS::get_singleton()->mesh_surface_get_array(mesh, surface); + stride = VS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets); + offset_vertices = surface_offsets[VS::ARRAY_VERTEX]; + offset_normal = surface_offsets[VS::ARRAY_NORMAL]; +} + +void SoftBodyVisualServerHandler::clear() { + + if (mesh.is_valid()) { + buffer.resize(0); + } + + mesh = RID(); +} + +void SoftBodyVisualServerHandler::open() { + write_buffer = buffer.write(); +} + +void SoftBodyVisualServerHandler::close() { + write_buffer = PoolVector<uint8_t>::Write(); +} + +void SoftBodyVisualServerHandler::commit_changes() { + VS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer); +} + +void SoftBodyVisualServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { + copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); +} + +void SoftBodyVisualServerHandler::set_aabb(const AABB &p_aabb) { + VS::get_singleton()->mesh_set_custom_aabb(mesh, p_aabb); +} + +SoftBody::PinnedPoint::PinnedPoint() : + point_index(-1), + spatial_attachment(NULL) { +} + +SoftBody::PinnedPoint::PinnedPoint(const PinnedPoint &obj_tocopy) { + point_index = obj_tocopy.point_index; + spatial_attachment_path = obj_tocopy.spatial_attachment_path; + spatial_attachment = obj_tocopy.spatial_attachment; + offset = obj_tocopy.offset; +} + +void SoftBody::_update_pickable() { + if (!is_inside_tree()) + return; + bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree(); + PhysicsServer::get_singleton()->soft_body_set_ray_pickable(physics_rid, pickable); +} + +bool SoftBody::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + + return _set_property_pinned_points_indices(p_value); + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _set_property_pinned_points_attachment(idx, what, p_value); + } + + return false; +} + +bool SoftBody::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + String which = name.get_slicec('/', 0); + + if ("pinned_points" == which) { + Array arr_ret; + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + arr_ret.resize(pinned_points_indices_size); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + arr_ret[i] = r[i].point_index; + } + + r_ret = arr_ret; + return true; + + } else if ("attachments" == which) { + + int idx = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + + return _get_property_pinned_points(idx, what, r_ret); + } + + return false; +} + +void SoftBody::_get_property_list(List<PropertyInfo> *p_list) const { + + const int pinned_points_indices_size = pinned_points.size(); + + p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "pinned_points")); + + for (int i = 0; i < pinned_points_indices_size; ++i) { + p_list->push_back(PropertyInfo(Variant::INT, "attachments/" + itos(i) + "/point_index")); + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "attachments/" + itos(i) + "/spatial_attachment_path")); + p_list->push_back(PropertyInfo(Variant::VECTOR3, "attachments/" + itos(i) + "/offset")); + } +} + +bool SoftBody::_set_property_pinned_points_indices(const Array &p_indices) { + + const int p_indices_size = p_indices.size(); + + { // Remove the pined points on physics server that will be removed by resize + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + if (p_indices_size < pinned_points.size()) { + for (int i = pinned_points.size() - 1; i >= p_indices_size; --i) { + pin_point(r[i].point_index, false); + } + } + } + + pinned_points.resize(p_indices_size); + + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + int point_index; + for (int i = 0; i < p_indices_size; ++i) { + point_index = p_indices.get(i); + if (w[i].point_index != point_index) { + if (-1 != w[i].point_index) + pin_point(w[i].point_index, false); + w[i].point_index = point_index; + pin_point(w[i].point_index, true); + } + } + return true; +} + +bool SoftBody::_set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value) { + if (pinned_points.size() <= p_item) { + return false; + } + + if ("spatial_attachment_path" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + pin_point(w[p_item].point_index, true, p_value); + _make_cache_dirty(); + } else if ("offset" == p_what) { + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + w[p_item].offset = p_value; + } else { + return false; + } + + return true; +} + +bool SoftBody::_get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const { + if (pinned_points.size() <= p_item) { + return false; + } + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + + if ("point_index" == p_what) { + r_ret = r[p_item].point_index; + } else if ("spatial_attachment_path" == p_what) { + r_ret = r[p_item].spatial_attachment_path; + } else if ("offset" == p_what) { + r_ret = r[p_item].offset; + } else { + return false; + } + + return true; +} + +void SoftBody::_changed_callback(Object *p_changed, const char *p_prop) { + update_physics_server(); + _reset_points_offsets(); +#ifdef TOOLS_ENABLED + if (p_changed == this) { + update_configuration_warning(); + } +#endif +} + +void SoftBody::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_WORLD: { + + if (Engine::get_singleton()->is_editor_hint()) { + + add_change_receptor(this); + } + + RID space = get_world()->get_space(); + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, space); + update_physics_server(); + } break; + case NOTIFICATION_READY: { + if (!parent_collision_ignore.is_empty()) + add_collision_exception_with(get_node(parent_collision_ignore)); + + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + + if (Engine::get_singleton()->is_editor_hint()) { + _reset_points_offsets(); + return; + } + + PhysicsServer::get_singleton()->soft_body_set_transform(physics_rid, get_global_transform()); + + set_notify_transform(false); + // Required to be top level with Transform at center of world in order to modify VisualServer only to support custom Transform + set_as_toplevel(true); + set_transform(Transform()); + set_notify_transform(true); + + } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + + if (!simulation_started) + return; + + _update_cache_pin_points_datas(); + // Submit bone attachment + const int pinned_points_indices_size = pinned_points.size(); + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = 0; i < pinned_points_indices_size; ++i) { + if (r[i].spatial_attachment) { + PhysicsServer::get_singleton()->soft_body_move_point(physics_rid, r[i].point_index, r[i].spatial_attachment->get_global_transform().xform(r[i].offset)); + } + } + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + + _update_pickable(); + + } break; + case NOTIFICATION_EXIT_WORLD: { + + PhysicsServer::get_singleton()->soft_body_set_space(physics_rid, RID()); + + } break; + } + +#ifdef TOOLS_ENABLED + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warning(); + } + } + +#endif +} + +void SoftBody::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_draw_soft_mesh"), &SoftBody::_draw_soft_mesh); + + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody::get_collision_mask); + + ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody::get_collision_layer); + + ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody::set_collision_mask_bit); + ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody::get_collision_mask_bit); + + ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody::set_collision_layer_bit); + ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody::get_collision_layer_bit); + + ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody::set_parent_collision_ignore); + ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody::get_parent_collision_ignore); + + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody::remove_collision_exception_with); + + ClassDB::bind_method(D_METHOD("set_simulation_precision", "simulation_precision"), &SoftBody::set_simulation_precision); + ClassDB::bind_method(D_METHOD("get_simulation_precision"), &SoftBody::get_simulation_precision); + + ClassDB::bind_method(D_METHOD("set_total_mass", "mass"), &SoftBody::set_total_mass); + ClassDB::bind_method(D_METHOD("get_total_mass"), &SoftBody::get_total_mass); + + ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody::set_linear_stiffness); + ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody::get_linear_stiffness); + + ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody::set_areaAngular_stiffness); + ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody::get_areaAngular_stiffness); + + ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody::set_volume_stiffness); + ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody::get_volume_stiffness); + + ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody::set_pressure_coefficient); + ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody::get_pressure_coefficient); + + ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody::set_pose_matching_coefficient); + ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody::get_pose_matching_coefficient); + + ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody::set_damping_coefficient); + ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody::get_damping_coefficient); + + ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody::set_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody::get_drag_coefficient); + + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody::set_ray_pickable); + ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody::is_ray_pickable); + + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); + + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "parent_collision_ignore", PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE, "Parent collision object"), "set_parent_collision_ignore", "get_parent_collision_ignore"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_coefficient"); +} + +String SoftBody::get_configuration_warning() const { + + String warning = MeshInstance::get_configuration_warning(); + + if (get_mesh().is_null()) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("This body will be ignored until you set a mesh"); + } + + Transform t = get_transform(); + if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { + if (!warning.empty()) + warning += "\n\n"; + + warning += TTR("Size changes to SoftBody will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + } + + return warning; +} + +void SoftBody::_draw_soft_mesh() { + if (get_mesh().is_null()) + return; + + if (!visual_server_handler.is_ready()) { + + visual_server_handler.prepare(get_mesh()->get_rid(), 0); + + /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) + simulation_started = true; + call_deferred("set_as_toplevel", true); + call_deferred("set_transform", Transform()); + } + + visual_server_handler.open(); + PhysicsServer::get_singleton()->soft_body_update_visual_server(physics_rid, &visual_server_handler); + visual_server_handler.close(); + + visual_server_handler.commit_changes(); +} + +void SoftBody::update_physics_server() { + + if (Engine::get_singleton()->is_editor_hint()) { + + if (get_mesh().is_valid()) + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + else + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + + return; + } + + if (get_mesh().is_valid()) { + + become_mesh_owner(); + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()); + VS::get_singleton()->connect("frame_pre_draw", this, "_draw_soft_mesh"); + } else { + + PhysicsServer::get_singleton()->soft_body_set_mesh(physics_rid, NULL); + VS::get_singleton()->disconnect("frame_pre_draw", this, "_draw_soft_mesh"); + } +} + +void SoftBody::become_mesh_owner() { + if (mesh.is_null()) + return; + + if (!mesh_owner) { + mesh_owner = true; + + Vector<Ref<Material> > copy_materials; + copy_materials.append_array(materials); + + ERR_FAIL_COND(!mesh->get_surface_count()); + + // Get current mesh array and create new mesh array with necessary flag for softbody + Array surface_arrays = mesh->surface_get_arrays(0); + Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0); + uint32_t surface_format = mesh->surface_get_format(0); + + surface_format &= ~(Mesh::ARRAY_COMPRESS_VERTEX | Mesh::ARRAY_COMPRESS_NORMAL); + surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; + + Ref<ArrayMesh> soft_mesh; + soft_mesh.instance(); + soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_format); + soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); + + set_mesh(soft_mesh); + + for (int i = copy_materials.size() - 1; 0 <= i; --i) { + set_surface_material(i, copy_materials[i]); + } + } +} + +void SoftBody::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + PhysicsServer::get_singleton()->soft_body_set_collision_mask(physics_rid, p_mask); +} + +uint32_t SoftBody::get_collision_mask() const { + return collision_mask; +} +void SoftBody::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + PhysicsServer::get_singleton()->soft_body_set_collision_layer(physics_rid, p_layer); +} + +uint32_t SoftBody::get_collision_layer() const { + return collision_layer; +} + +void SoftBody::set_collision_mask_bit(int p_bit, bool p_value) { + uint32_t mask = get_collision_mask(); + if (p_value) + mask |= 1 << p_bit; + else + mask &= ~(1 << p_bit); + set_collision_mask(mask); +} + +bool SoftBody::get_collision_mask_bit(int p_bit) const { + return get_collision_mask() & (1 << p_bit); +} + +void SoftBody::set_collision_layer_bit(int p_bit, bool p_value) { + uint32_t layer = get_collision_layer(); + if (p_value) + layer |= 1 << p_bit; + else + layer &= ~(1 << p_bit); + set_collision_layer(layer); +} + +bool SoftBody::get_collision_layer_bit(int p_bit) const { + return get_collision_layer() & (1 << p_bit); +} + +void SoftBody::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { + parent_collision_ignore = p_parent_collision_ignore; +} + +const NodePath &SoftBody::get_parent_collision_ignore() const { + return parent_collision_ignore; +} + +void SoftBody::set_pinned_points_indices(PoolVector<SoftBody::PinnedPoint> p_pinned_points_indices) { + pinned_points = p_pinned_points_indices; + PoolVector<PinnedPoint>::Read w = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + pin_point(p_pinned_points_indices[i].point_index, true); + } +} + +PoolVector<SoftBody::PinnedPoint> SoftBody::get_pinned_points_indices() { + return pinned_points; +} + +void SoftBody::add_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_add_collision_exception(physics_rid, collision_object->get_rid()); +} + +void SoftBody::remove_collision_exception_with(Node *p_node) { + ERR_FAIL_NULL(p_node); + CollisionObject *collision_object = Object::cast_to<CollisionObject>(p_node); + if (!collision_object) { + ERR_EXPLAIN("Collision exception only works between two CollisionObject"); + } + ERR_FAIL_COND(!collision_object); + PhysicsServer::get_singleton()->soft_body_remove_collision_exception(physics_rid, collision_object->get_rid()); +} + +int SoftBody::get_simulation_precision() { + return PhysicsServer::get_singleton()->soft_body_get_simulation_precision(physics_rid); +} + +void SoftBody::set_simulation_precision(int p_simulation_precision) { + PhysicsServer::get_singleton()->soft_body_set_simulation_precision(physics_rid, p_simulation_precision); +} + +real_t SoftBody::get_total_mass() { + return PhysicsServer::get_singleton()->soft_body_get_total_mass(physics_rid); +} + +void SoftBody::set_total_mass(real_t p_total_mass) { + PhysicsServer::get_singleton()->soft_body_set_total_mass(physics_rid, p_total_mass); +} + +void SoftBody::set_linear_stiffness(real_t p_linear_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_linear_stiffness(physics_rid, p_linear_stiffness); +} + +real_t SoftBody::get_linear_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_linear_stiffness(physics_rid); +} + +void SoftBody::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness); +} + +real_t SoftBody::get_areaAngular_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid); +} + +void SoftBody::set_volume_stiffness(real_t p_volume_stiffness) { + PhysicsServer::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness); +} + +real_t SoftBody::get_volume_stiffness() { + return PhysicsServer::get_singleton()->soft_body_get_volume_stiffness(physics_rid); +} + +real_t SoftBody::get_pressure_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); +} + +void SoftBody::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient); +} + +real_t SoftBody::get_pose_matching_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid); +} + +void SoftBody::set_pressure_coefficient(real_t p_pressure_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); +} + +real_t SoftBody::get_damping_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_damping_coefficient(physics_rid); +} + +void SoftBody::set_damping_coefficient(real_t p_damping_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_damping_coefficient(physics_rid, p_damping_coefficient); +} + +real_t SoftBody::get_drag_coefficient() { + return PhysicsServer::get_singleton()->soft_body_get_drag_coefficient(physics_rid); +} + +void SoftBody::set_drag_coefficient(real_t p_drag_coefficient) { + PhysicsServer::get_singleton()->soft_body_set_drag_coefficient(physics_rid, p_drag_coefficient); +} + +Vector3 SoftBody::get_point_transform(int p_point_index) { + return PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, p_point_index); +} + +void SoftBody::pin_point_toggle(int p_point_index) { + pin_point(p_point_index, !(-1 != _has_pinned_point(p_point_index))); +} + +void SoftBody::pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path) { + _pin_point_on_physics_server(p_point_index, pin); + if (pin) { + _add_pinned_point(p_point_index, p_spatial_attachment_path); + } else { + _remove_pinned_point(p_point_index); + } +} + +bool SoftBody::is_point_pinned(int p_point_index) const { + return -1 != _has_pinned_point(p_point_index); +} + +void SoftBody::set_ray_pickable(bool p_ray_pickable) { + + ray_pickable = p_ray_pickable; + _update_pickable(); +} + +bool SoftBody::is_ray_pickable() const { + + return ray_pickable; +} + +SoftBody::SoftBody() : + MeshInstance(), + physics_rid(PhysicsServer::get_singleton()->soft_body_create()), + mesh_owner(false), + collision_mask(1), + collision_layer(1), + simulation_started(false), + pinned_points_cache_dirty(true) { + + PhysicsServer::get_singleton()->body_attach_object_instance_id(physics_rid, get_instance_id()); + //set_notify_transform(true); + set_physics_process_internal(true); +} + +SoftBody::~SoftBody() { +} + +void SoftBody::reset_softbody_pin() { + PhysicsServer::get_singleton()->soft_body_remove_all_pinned_points(physics_rid); + PoolVector<PinnedPoint>::Read pps = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 < i; --i) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, pps[i].point_index, true); + } +} + +void SoftBody::_make_cache_dirty() { + pinned_points_cache_dirty = true; +} + +void SoftBody::_update_cache_pin_points_datas() { + if (!pinned_points_cache_dirty) + return; + + pinned_points_cache_dirty = false; + + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!w[i].spatial_attachment_path.is_empty()) { + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(w[i].spatial_attachment_path)); + } + if (!w[i].spatial_attachment) { + ERR_PRINT("Spatial node not defined in the pinned point, Softbody undefined behaviour!"); + } + } +} + +void SoftBody::_pin_point_on_physics_server(int p_point_index, bool pin) { + PhysicsServer::get_singleton()->soft_body_pin_point(physics_rid, p_point_index, pin); +} + +void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) { + SoftBody::PinnedPoint *pinned_point; + if (-1 == _get_pinned_point(p_point_index, pinned_point)) { + + // Create new + PinnedPoint pp; + pp.point_index = p_point_index; + pp.spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pp.spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index)); + } + + pinned_points.push_back(pp); + + } else { + + pinned_point->point_index = p_point_index; + pinned_point->spatial_attachment_path = p_spatial_attachment_path; + + if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) { + pinned_point->spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path)); + pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index)); + } + } +} + +void SoftBody::_reset_points_offsets() { + + if (!Engine::get_singleton()->is_editor_hint()) + return; + + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + PoolVector<PinnedPoint>::Write w = pinned_points.write(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + + if (!r[i].spatial_attachment) + w[i].spatial_attachment = Object::cast_to<Spatial>(get_node(r[i].spatial_attachment_path)); + + if (!r[i].spatial_attachment) + continue; + + w[i].offset = (r[i].spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, r[i].point_index)); + } +} + +void SoftBody::_remove_pinned_point(int p_point_index) { + const int id(_has_pinned_point(p_point_index)); + if (-1 != id) { + pinned_points.remove(id); + } +} + +int SoftBody::_get_pinned_point(int p_point_index, SoftBody::PinnedPoint *&r_point) const { + const int id = _has_pinned_point(p_point_index); + if (-1 == id) { + r_point = NULL; + return -1; + } else { + r_point = const_cast<SoftBody::PinnedPoint *>(&pinned_points.read()[id]); + return id; + } +} + +int SoftBody::_has_pinned_point(int p_point_index) const { + PoolVector<PinnedPoint>::Read r = pinned_points.read(); + for (int i = pinned_points.size() - 1; 0 <= i; --i) { + if (p_point_index == r[i].point_index) { + return i; + } + } + return -1; +} diff --git a/scene/3d/soft_body.h b/scene/3d/soft_body.h new file mode 100644 index 0000000000..cee32b9651 --- /dev/null +++ b/scene/3d/soft_body.h @@ -0,0 +1,200 @@ +/*************************************************************************/ +/* soft_physics_body.h */ +/* Author: AndreaCatania */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SOFT_PHYSICS_BODY_H +#define SOFT_PHYSICS_BODY_H + +#include "scene/3d/mesh_instance.h" + +class SoftBody; + +class SoftBodyVisualServerHandler { + + friend class SoftBody; + + RID mesh; + int surface; + PoolVector<uint8_t> buffer; + uint32_t stride; + uint32_t offset_vertices; + uint32_t offset_normal; + + PoolVector<uint8_t>::Write write_buffer; + +private: + SoftBodyVisualServerHandler(); + bool is_ready() { return mesh.is_valid(); } + void prepare(RID p_mesh_rid, int p_surface); + void clear(); + void open(); + void close(); + void commit_changes(); + +public: + void set_vertex(int p_vertex_id, const void *p_vector3); + void set_normal(int p_vertex_id, const void *p_vector3); + void set_aabb(const AABB &p_aabb); +}; + +class SoftBody : public MeshInstance { + GDCLASS(SoftBody, MeshInstance); + +public: + struct PinnedPoint { + int point_index; + NodePath spatial_attachment_path; + Spatial *spatial_attachment; // Cache + Vector3 offset; + + PinnedPoint(); + PinnedPoint(const PinnedPoint &obj_tocopy); + }; + +private: + SoftBodyVisualServerHandler visual_server_handler; + + RID physics_rid; + + bool mesh_owner; + uint32_t collision_mask; + uint32_t collision_layer; + NodePath parent_collision_ignore; + PoolVector<PinnedPoint> pinned_points; + bool simulation_started; + bool pinned_points_cache_dirty; + + Ref<ArrayMesh> debug_mesh_cache; + class MeshInstance *debug_mesh; + + bool capture_input_on_drag; + bool ray_pickable; + + void _update_pickable(); + +protected: + 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; + + bool _set_property_pinned_points_indices(const Array &p_indices); + bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value); + bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const; + + virtual void _changed_callback(Object *p_changed, const char *p_prop); + + void _notification(int p_what); + static void _bind_methods(); + + virtual String get_configuration_warning() const; + +protected: + void _draw_soft_mesh(); + +public: + void update_physics_server(); + void become_mesh_owner(); + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + + void set_collision_layer_bit(int p_bit, bool p_value); + bool get_collision_layer_bit(int p_bit) const; + + void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore); + const NodePath &get_parent_collision_ignore() const; + + void set_pinned_points_indices(PoolVector<PinnedPoint> p_pinned_points_indices); + PoolVector<PinnedPoint> get_pinned_points_indices(); + + void set_simulation_precision(int p_simulation_precision); + int get_simulation_precision(); + + void set_total_mass(real_t p_total_mass); + real_t get_total_mass(); + + void set_linear_stiffness(real_t p_linear_stiffness); + real_t get_linear_stiffness(); + + void set_areaAngular_stiffness(real_t p_areaAngular_stiffness); + real_t get_areaAngular_stiffness(); + + void set_volume_stiffness(real_t p_volume_stiffness); + real_t get_volume_stiffness(); + + void set_pressure_coefficient(real_t p_pressure_coefficient); + real_t get_pressure_coefficient(); + + void set_pose_matching_coefficient(real_t p_pose_matching_coefficient); + real_t get_pose_matching_coefficient(); + + void set_damping_coefficient(real_t p_damping_coefficient); + real_t get_damping_coefficient(); + + void set_drag_coefficient(real_t p_drag_coefficient); + real_t get_drag_coefficient(); + + void add_collision_exception_with(Node *p_node); + void remove_collision_exception_with(Node *p_node); + + Vector3 get_point_transform(int p_point_index); + + void pin_point_toggle(int p_point_index); + void pin_point(int p_point_index, bool pin, const NodePath &p_spatial_attachment_path = NodePath()); + bool is_point_pinned(int p_point_index) const; + + void set_ray_pickable(bool p_ray_pickable); + bool is_ray_pickable() const; + + SoftBody(); + ~SoftBody(); + +private: + void reset_softbody_pin(); + + void _make_cache_dirty(); + void _update_cache_pin_points_datas(); + + void _pin_point_on_physics_server(int p_point_index, bool pin); + void _add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path); + void _reset_points_offsets(); + + void _remove_pinned_point(int p_point_index); + int _get_pinned_point(int p_point_index, PinnedPoint *&r_point) const; + int _has_pinned_point(int p_point_index) const; +}; + +#endif // SOFT_PHYSICS_BODY_H diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 748aa8aad4..3f494264e7 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -202,6 +202,7 @@ void Spatial::_notification(int p_what) { #ifdef TOOLS_ENABLED if (data.gizmo.is_valid()) { data.gizmo->free(); + data.gizmo.unref(); } #endif @@ -280,6 +281,10 @@ Transform Spatial::get_global_transform() const { data.global_transform = data.local_transform; } + if (data.disable_scale) { + data.global_transform.basis.orthonormalize(); + } + data.dirty &= ~DIRTY_GLOBAL; } @@ -467,6 +472,15 @@ void Spatial::set_disable_gizmo(bool p_enabled) { #endif +void Spatial::set_disable_scale(bool p_enabled) { + + data.disable_scale = p_enabled; +} + +bool Spatial::is_scale_disabled() const { + return data.disable_scale; +} + void Spatial::set_as_toplevel(bool p_enabled) { if (data.toplevel == p_enabled) @@ -632,19 +646,15 @@ void Spatial::scale_object_local(const Vector3 &p_scale) { void Spatial::global_rotate(const Vector3 &p_axis, float p_angle) { - Basis rotation(p_axis, p_angle); Transform t = get_global_transform(); - t.basis = rotation * t.basis; + t.basis.rotate(p_axis, p_angle); set_global_transform(t); } void Spatial::global_scale(const Vector3 &p_scale) { - Basis s; - s.set_scale(p_scale); - Transform t = get_global_transform(); - t.basis = s * t.basis; + t.basis.scale(p_scale); set_global_transform(t); } @@ -735,6 +745,8 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Spatial::set_ignore_transform_notification); ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Spatial::set_as_toplevel); ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel); + ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Spatial::set_disable_scale); + ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Spatial::is_scale_disabled); ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world); ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo); @@ -755,15 +767,6 @@ void Spatial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_notify_transform", "enable"), &Spatial::set_notify_transform); ClassDB::bind_method(D_METHOD("is_transform_notification_enabled"), &Spatial::is_transform_notification_enabled); - void rotate(const Vector3 &p_axis, float p_angle); - void rotate_x(float p_angle); - void rotate_y(float p_angle); - void rotate_z(float p_angle); - void translate(const Vector3 &p_offset); - void scale(const Vector3 &p_ratio); - void global_rotate(const Vector3 &p_axis, float p_angle); - void global_translate(const Vector3 &p_offset); - ClassDB::bind_method(D_METHOD("rotate", "axis", "angle"), &Spatial::rotate); ClassDB::bind_method(D_METHOD("global_rotate", "axis", "angle"), &Spatial::global_rotate); ClassDB::bind_method(D_METHOD("global_scale", "scale"), &Spatial::global_scale); @@ -791,12 +794,13 @@ void Spatial::_bind_methods() { //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ; ADD_GROUP("Transform", ""); - ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_translation", "get_translation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); + ADD_GROUP("Matrix", ""); + ADD_PROPERTYNZ(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo"); @@ -817,6 +821,7 @@ Spatial::Spatial() : data.viewport = NULL; data.inside_world = false; data.visible = true; + data.disable_scale = false; #ifdef TOOLS_ENABLED data.gizmo_disabled = false; diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index a43bed3e4a..bc054a8763 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -51,6 +51,7 @@ public: virtual bool can_draw() const = 0; SpatialGizmo(); + virtual ~SpatialGizmo() {} }; class Spatial : public Node { @@ -92,6 +93,7 @@ class Spatial : public Node { bool notify_transform; bool visible; + bool disable_scale; #ifdef TOOLS_ENABLED Ref<SpatialGizmo> gizmo; @@ -153,6 +155,9 @@ public: void set_as_toplevel(bool p_enabled); bool is_set_as_toplevel() const; + void set_disable_scale(bool p_enabled); + bool is_scale_disabled() const; + void set_disable_gizmo(bool p_enabled); void update_gizmo(); void set_gizmo(const Ref<SpatialGizmo> &p_gizmo); diff --git a/scene/3d/spatial_velocity_tracker.cpp b/scene/3d/spatial_velocity_tracker.cpp index c547e76e30..d96b003a81 100644 --- a/scene/3d/spatial_velocity_tracker.cpp +++ b/scene/3d/spatial_velocity_tracker.cpp @@ -53,11 +53,11 @@ void SpatialVelocityTracker::update_position(const Vector3 &p_position) { if (position_history_len == 0 || position_history[0].frame != ph.frame) { //in same frame, use latest position_history_len = MIN(position_history.size(), position_history_len + 1); for (int i = position_history_len - 1; i > 0; i--) { - position_history[i] = position_history[i - 1]; + position_history.write[i] = position_history[i - 1]; } } - position_history[0] = ph; + position_history.write[0] = ph; } Vector3 SpatialVelocityTracker::get_tracked_linear_velocity() const { @@ -114,7 +114,7 @@ void SpatialVelocityTracker::reset(const Vector3 &p_new_pos) { ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); } - position_history[0] = ph; + position_history.write[0] = ph; position_history_len = 1; } diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index 385956dc16..26958930e4 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) { //bilateral constraint between two dynamic objects void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, - PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) { + PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence) { real_t normalLenSqr = normal.length_squared(); //ERR_FAIL_COND( normalLenSqr < real_t(1.1)); @@ -677,8 +677,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { //collapse all those loops into one! for (int i = 0; i < wheels.size(); i++) { - m_sideImpulse[i] = real_t(0.); - m_forwardImpulse[i] = real_t(0.); + m_sideImpulse.write[i] = real_t(0.); + m_forwardImpulse.write[i] = real_t(0.); } { @@ -693,22 +693,22 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { Basis wheelBasis0 = wheelInfo.m_worldTransform.basis; //get_global_transform().basis; - m_axle[i] = wheelBasis0.get_axis(Vector3::AXIS_X); + m_axle.write[i] = wheelBasis0.get_axis(Vector3::AXIS_X); //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS; const Vector3 &surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS; real_t proj = m_axle[i].dot(surfNormalWS); - m_axle[i] -= surfNormalWS * proj; - m_axle[i] = m_axle[i].normalized(); + m_axle.write[i] -= surfNormalWS * proj; + m_axle.write[i] = m_axle[i].normalized(); - m_forwardWS[i] = surfNormalWS.cross(m_axle[i]); - m_forwardWS[i].normalize(); + m_forwardWS.write[i] = surfNormalWS.cross(m_axle[i]); + m_forwardWS.write[i].normalize(); _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS, wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS, - m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence); + m_axle[i], m_sideImpulse.write[i], wheelInfo.m_rollInfluence); - m_sideImpulse[i] *= sideFrictionStiffness2; + m_sideImpulse.write[i] *= sideFrictionStiffness2; } } } @@ -739,7 +739,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break) - m_forwardImpulse[wheel] = real_t(0.); + m_forwardImpulse.write[wheel] = real_t(0.); wheelInfo.m_skidInfo = real_t(1.); if (wheelInfo.m_raycastInfo.m_isInContact) { @@ -750,7 +750,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { real_t maximpSquared = maximp * maximpSide; - m_forwardImpulse[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; + m_forwardImpulse.write[wheel] = rollingFriction; //wheelInfo.m_engineForce* timeStep; real_t x = (m_forwardImpulse[wheel]) * fwdFactor; real_t y = (m_sideImpulse[wheel]) * sideFactor; @@ -772,8 +772,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { for (int wheel = 0; wheel < wheels.size(); wheel++) { if (m_sideImpulse[wheel] != real_t(0.)) { if (wheels[wheel]->m_skidInfo < real_t(1.)) { - m_forwardImpulse[wheel] *= wheels[wheel]->m_skidInfo; - m_sideImpulse[wheel] *= wheels[wheel]->m_skidInfo; + m_forwardImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; + m_sideImpulse.write[wheel] *= wheels[wheel]->m_skidInfo; } } } @@ -942,8 +942,6 @@ VehicleBody::VehicleBody() : engine_force = 0; brake = 0; - friction = 1; - state = NULL; ccd = false; diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index 1ac3693cc4..68fbf8d873 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -168,7 +168,7 @@ class VehicleBody : public RigidBody { btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); }; - void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence); + void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, const real_t p_rollInfluence); real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint); void _update_friction(PhysicsDirectBodyState *s); diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 00541a7d8a..767518dc83 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -105,12 +105,28 @@ uint32_t VisualInstance::get_layer_mask() const { return layers; } +void VisualInstance::set_layer_mask_bit(int p_layer, bool p_enable) { + ERR_FAIL_INDEX(p_layer, 32); + if (p_enable) { + set_layer_mask(layers | (1 << p_layer)); + } else { + set_layer_mask(layers & (~(1 << p_layer))); + } +} + +bool VisualInstance::get_layer_mask_bit(int p_layer) const { + ERR_FAIL_INDEX_V(p_layer, 32, false); + return (layers & (1 << p_layer)); +} + void VisualInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_visual_instance_rid"), &VisualInstance::_get_visual_instance_rid); ClassDB::bind_method(D_METHOD("set_base", "base"), &VisualInstance::set_base); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance::get_layer_mask); + ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance::set_layer_mask_bit); + ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance::get_layer_mask_bit); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance::get_transformed_aabb); diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h index 8458a343b2..9249bc04ce 100644 --- a/scene/3d/visual_instance.h +++ b/scene/3d/visual_instance.h @@ -73,6 +73,9 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; + void set_layer_mask_bit(int p_layer, bool p_enable); + bool get_layer_mask_bit(int p_layer) const; + VisualInstance(); ~VisualInstance(); }; diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index 670df5cc7f..f3abdc6bbe 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -113,7 +113,7 @@ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) { rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \ if (min > rad || max < -rad) return false; - /*======================== Z-tests ========================*/ +/*======================== Z-tests ========================*/ #define AXISTEST_Z12(a, b, fa, fb) \ p1 = a * v1.x - b * v1.y; \ @@ -409,16 +409,16 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p } //put this temporarily here, corrected in a later step - bake_cells[p_idx].albedo[0] += albedo_accum.r; - bake_cells[p_idx].albedo[1] += albedo_accum.g; - bake_cells[p_idx].albedo[2] += albedo_accum.b; - bake_cells[p_idx].emission[0] += emission_accum.r; - bake_cells[p_idx].emission[1] += emission_accum.g; - bake_cells[p_idx].emission[2] += emission_accum.b; - bake_cells[p_idx].normal[0] += normal_accum.x; - bake_cells[p_idx].normal[1] += normal_accum.y; - bake_cells[p_idx].normal[2] += normal_accum.z; - bake_cells[p_idx].alpha += alpha; + bake_cells.write[p_idx].albedo[0] += albedo_accum.r; + bake_cells.write[p_idx].albedo[1] += albedo_accum.g; + bake_cells.write[p_idx].albedo[2] += albedo_accum.b; + bake_cells.write[p_idx].emission[0] += emission_accum.r; + bake_cells.write[p_idx].emission[1] += emission_accum.g; + bake_cells.write[p_idx].emission[2] += emission_accum.b; + bake_cells.write[p_idx].normal[0] += normal_accum.x; + bake_cells.write[p_idx].normal[1] += normal_accum.y; + bake_cells.write[p_idx].normal[2] += normal_accum.z; + bake_cells.write[p_idx].alpha += alpha; } else { //go down @@ -465,9 +465,9 @@ void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p //sub cell must be created uint32_t child_idx = bake_cells.size(); - bake_cells[p_idx].children[i] = child_idx; + bake_cells.write[p_idx].children[i] = child_idx; bake_cells.resize(bake_cells.size() + 1); - bake_cells[child_idx].level = p_level + 1; + bake_cells.write[child_idx].level = p_level + 1; } _plot_face(bake_cells[p_idx].children[i], p_level + 1, nx, ny, nz, p_vtx, p_normal, p_uv, p_material, aabb); @@ -483,7 +483,7 @@ Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color ret.resize(bake_texture_size * bake_texture_size); for (int i = 0; i < bake_texture_size * bake_texture_size; i++) { - ret[i] = p_color_add; + ret.write[i] = p_color_add; } return ret; @@ -509,7 +509,7 @@ Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color c.a = r[i * 4 + 3] / 255.0; - ret[i] = c; + ret.write[i] = c; } return ret; @@ -686,13 +686,13 @@ void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, con void VoxelLightBaker::_init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent) { - bake_light[p_idx].x = p_x; - bake_light[p_idx].y = p_y; - bake_light[p_idx].z = p_z; + bake_light.write[p_idx].x = p_x; + bake_light.write[p_idx].y = p_y; + bake_light.write[p_idx].z = p_z; if (p_level == cell_subdiv - 1) { - bake_light[p_idx].next_leaf = first_leaf; + bake_light.write[p_idx].next_leaf = first_leaf; first_leaf = p_idx; } else { @@ -1197,33 +1197,33 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { leaf_voxel_count++; float alpha = bake_cells[p_idx].alpha; - bake_cells[p_idx].albedo[0] /= alpha; - bake_cells[p_idx].albedo[1] /= alpha; - bake_cells[p_idx].albedo[2] /= alpha; + bake_cells.write[p_idx].albedo[0] /= alpha; + bake_cells.write[p_idx].albedo[1] /= alpha; + bake_cells.write[p_idx].albedo[2] /= alpha; //transfer emission to light - bake_cells[p_idx].emission[0] /= alpha; - bake_cells[p_idx].emission[1] /= alpha; - bake_cells[p_idx].emission[2] /= alpha; + bake_cells.write[p_idx].emission[0] /= alpha; + bake_cells.write[p_idx].emission[1] /= alpha; + bake_cells.write[p_idx].emission[2] /= alpha; - bake_cells[p_idx].normal[0] /= alpha; - bake_cells[p_idx].normal[1] /= alpha; - bake_cells[p_idx].normal[2] /= alpha; + bake_cells.write[p_idx].normal[0] /= alpha; + bake_cells.write[p_idx].normal[1] /= alpha; + bake_cells.write[p_idx].normal[2] /= alpha; Vector3 n(bake_cells[p_idx].normal[0], bake_cells[p_idx].normal[1], bake_cells[p_idx].normal[2]); if (n.length() < 0.01) { //too much fight over normal, zero it - bake_cells[p_idx].normal[0] = 0; - bake_cells[p_idx].normal[1] = 0; - bake_cells[p_idx].normal[2] = 0; + bake_cells.write[p_idx].normal[0] = 0; + bake_cells.write[p_idx].normal[1] = 0; + bake_cells.write[p_idx].normal[2] = 0; } else { n.normalize(); - bake_cells[p_idx].normal[0] = n.x; - bake_cells[p_idx].normal[1] = n.y; - bake_cells[p_idx].normal[2] = n.z; + bake_cells.write[p_idx].normal[0] = n.x; + bake_cells.write[p_idx].normal[1] = n.y; + bake_cells.write[p_idx].normal[2] = n.z; } - bake_cells[p_idx].alpha = 1.0; + bake_cells.write[p_idx].alpha = 1.0; /*if (bake_light.size()) { for(int i=0;i<6;i++) { @@ -1235,20 +1235,20 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { //go down - bake_cells[p_idx].emission[0] = 0; - bake_cells[p_idx].emission[1] = 0; - bake_cells[p_idx].emission[2] = 0; - bake_cells[p_idx].normal[0] = 0; - bake_cells[p_idx].normal[1] = 0; - bake_cells[p_idx].normal[2] = 0; - bake_cells[p_idx].albedo[0] = 0; - bake_cells[p_idx].albedo[1] = 0; - bake_cells[p_idx].albedo[2] = 0; + bake_cells.write[p_idx].emission[0] = 0; + bake_cells.write[p_idx].emission[1] = 0; + bake_cells.write[p_idx].emission[2] = 0; + bake_cells.write[p_idx].normal[0] = 0; + bake_cells.write[p_idx].normal[1] = 0; + bake_cells.write[p_idx].normal[2] = 0; + bake_cells.write[p_idx].albedo[0] = 0; + bake_cells.write[p_idx].albedo[1] = 0; + bake_cells.write[p_idx].albedo[2] = 0; if (bake_light.size()) { for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] = 0; - bake_light[p_idx].accum[j][1] = 0; - bake_light[p_idx].accum[j][2] = 0; + bake_light.write[p_idx].accum[j][0] = 0; + bake_light.write[p_idx].accum[j][1] = 0; + bake_light.write[p_idx].accum[j][2] = 0; } } @@ -1267,29 +1267,29 @@ void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) { if (bake_light.size() > 0) { for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] += bake_light[child].accum[j][0]; - bake_light[p_idx].accum[j][1] += bake_light[child].accum[j][1]; - bake_light[p_idx].accum[j][2] += bake_light[child].accum[j][2]; + bake_light.write[p_idx].accum[j][0] += bake_light[child].accum[j][0]; + bake_light.write[p_idx].accum[j][1] += bake_light[child].accum[j][1]; + bake_light.write[p_idx].accum[j][2] += bake_light[child].accum[j][2]; } - bake_cells[p_idx].emission[0] += bake_cells[child].emission[0]; - bake_cells[p_idx].emission[1] += bake_cells[child].emission[1]; - bake_cells[p_idx].emission[2] += bake_cells[child].emission[2]; + bake_cells.write[p_idx].emission[0] += bake_cells[child].emission[0]; + bake_cells.write[p_idx].emission[1] += bake_cells[child].emission[1]; + bake_cells.write[p_idx].emission[2] += bake_cells[child].emission[2]; } children_found++; } - bake_cells[p_idx].alpha = alpha_average / 8.0; + bake_cells.write[p_idx].alpha = alpha_average / 8.0; if (bake_light.size() && children_found) { float divisor = Math::lerp(8, children_found, propagation); for (int j = 0; j < 6; j++) { - bake_light[p_idx].accum[j][0] /= divisor; - bake_light[p_idx].accum[j][1] /= divisor; - bake_light[p_idx].accum[j][2] /= divisor; + bake_light.write[p_idx].accum[j][0] /= divisor; + bake_light.write[p_idx].accum[j][1] /= divisor; + bake_light.write[p_idx].accum[j][2] /= divisor; } - bake_cells[p_idx].emission[0] /= divisor; - bake_cells[p_idx].emission[1] /= divisor; - bake_cells[p_idx].emission[2] /= divisor; + bake_cells.write[p_idx].emission[0] /= divisor; + bake_cells.write[p_idx].emission[1] /= divisor; + bake_cells.write[p_idx].emission[2] /= divisor; } } } @@ -1961,7 +1961,7 @@ Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh #endif for (int i = 0; i < height; i++) { - //print_line("bake line " + itos(i) + " / " + itos(height)); + //print_line("bake line " + itos(i) + " / " + itos(height)); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 1) #endif @@ -2403,25 +2403,25 @@ PoolVector<uint8_t> VoxelLightBaker::create_capture_octree(int p_subdiv) { new_size++; demap.push_back(i); } - remap[i] = c; + remap.write[i] = c; } Vector<VoxelLightBakerOctree> octree; octree.resize(new_size); for (int i = 0; i < new_size; i++) { - octree[i].alpha = bake_cells[demap[i]].alpha; + octree.write[i].alpha = bake_cells[demap[i]].alpha; for (int j = 0; j < 6; j++) { for (int k = 0; k < 3; k++) { float l = bake_light[demap[i]].accum[j][k]; //add anisotropic light l += bake_cells[demap[i]].emission[k]; //add emission - octree[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree + octree.write[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree } } for (int j = 0; j < 8; j++) { uint32_t child = bake_cells[demap[i]].children[j]; - octree[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child]; + octree.write[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child]; } } diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index d3d2870c3f..1bc9fa4b12 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -4,10 +4,9 @@ void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) { AnimationRootNode::set_tree(p_player); - for(int i=0;i<blend_points_used;i++) { + for (int i = 0; i < blend_points_used; i++) { blend_points[i].node->set_tree(p_player); } - } void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 774894ef4b..d1ed4c6a1f 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -34,7 +34,6 @@ protected: static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1); diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 82db647124..3c93a0c8ec 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -4,12 +4,11 @@ void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) { AnimationRootNode::set_tree(p_player); - for(int i=0;i<blend_points_used;i++) { + for (int i = 0; i < blend_points_used; i++) { blend_points[i].node->set_tree(p_player); } } - void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); @@ -25,7 +24,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ for (int i = 0; i < triangles.size(); i++) { for (int j = 0; j < 3; j++) { if (triangles[i].points[j] >= p_at_index) { - triangles[i].points[j]++; + triangles.write[i].points[j]++; } } } @@ -82,7 +81,7 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { erase = true; break; } else if (triangles[i].points[j] > p_point) { - triangles[i].points[j]--; + triangles.write[i].points[j]--; } } if (erase) { @@ -265,9 +264,9 @@ Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const { t.resize(triangles.size() * 3); for (int i = 0; i < triangles.size(); i++) { - t[i * 3 + 0] = triangles[i].points[0]; - t[i * 3 + 1] = triangles[i].points[1]; - t[i * 3 + 2] = triangles[i].points[2]; + t.write[i * 3 + 0] = triangles[i].points[0]; + t.write[i * 3 + 1] = triangles[i].points[1]; + t.write[i * 3 + 2] = triangles[i].points[2]; } return t; } @@ -285,7 +284,7 @@ void AnimationNodeBlendSpace2D::_update_triangles() { Vector<Vector2> points; points.resize(blend_points_used); for (int i = 0; i < blend_points_used; i++) { - points[i] = blend_points[i].position; + points.write[i] = blend_points[i].position; } Vector<Delaunay2D::Triangle> triangles = Delaunay2D::triangulate(points); diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 4778299df1..74d20b6013 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -47,7 +47,6 @@ protected: static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 6dcd5ca8ea..65904410d3 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -288,8 +288,8 @@ void AnimationNodeOneShot::_bind_methods() { ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); - BIND_CONSTANT(MIX_MODE_BLEND) - BIND_CONSTANT(MIX_MODE_ADD) + BIND_ENUM_CONSTANT(MIX_MODE_BLEND) + BIND_ENUM_CONSTANT(MIX_MODE_ADD) } AnimationNodeOneShot::AnimationNodeOneShot() { diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index 949a0be3bc..756907c41c 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -43,7 +43,7 @@ void AnimationCache::_node_exit_tree(Node *p_node) { if (path_cache[i].node != p_node) continue; - path_cache[i].valid = false; //invalidate path cache + path_cache.write[i].valid = false; //invalidate path cache } } @@ -196,7 +196,7 @@ void AnimationCache::set_track_transform(int p_idx, const Transform &p_transform ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -217,7 +217,7 @@ void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -232,7 +232,7 @@ void AnimationCache::call_track(int p_idx, const StringName &p_method, const Var ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -297,11 +297,11 @@ void AnimationCache::set_all(float p_time, float p_delta) { call_track(i, name, NULL, 0, err); } else { - Vector<Variant *> argptrs; + Vector<const Variant *> argptrs; argptrs.resize(args.size()); for (int j = 0; j < args.size(); j++) { - argptrs[j] = &args[j]; + argptrs.write[j] = &args.write[j]; } call_track(i, name, (const Variant **)&argptrs[0], args.size(), err); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index c5ad980806..f478112a36 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -71,9 +71,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); - BIND_CONSTANT(SWITCH_MODE_IMMEDIATE); - BIND_CONSTANT(SWITCH_MODE_SYNC); - BIND_CONSTANT(SWITCH_MODE_AT_END); + BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); + BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); + BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -169,11 +169,11 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri for (int i = 0; i < transitions.size(); i++) { if (transitions[i].from == p_name) { - transitions[i].from = p_new_name; + transitions.write[i].from = p_new_name; } if (transitions[i].to == p_name) { - transitions[i].to = p_new_name; + transitions.write[i].to = p_new_name; } } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index eac2c8d0c1..2782354432 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -233,7 +233,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { for (int i = 0; i < a->get_track_count(); i++) { - p_anim->node_cache[i] = NULL; + p_anim->node_cache.write[i] = NULL; RES resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); @@ -265,12 +265,12 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (node_cache_map.has(key)) { - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; } else { node_cache_map[key] = TrackNodeCache(); - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; p_anim->node_cache[i]->path = a->track_get_path(i); p_anim->node_cache[i]->node = child; p_anim->node_cache[i]->resource = resource; @@ -331,11 +331,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::BezierAnim ba; - String path = leftover_path[leftover_path.size() - 1]; - Vector<String> index = path.split("."); - for (int j = 0; j < index.size(); j++) { - ba.bezier_property.push_back(index[j]); - } + ba.bezier_property = leftover_path; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.owner = p_anim->node_cache[i]; @@ -549,6 +545,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float int s = params.size(); ERR_CONTINUE(s > VARIANT_ARG_MAX); +#ifdef DEBUG_ENABLED + if (!nc->node->has_method(method)) { + ERR_PRINTS("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif + if (can_call) { MessageQueue::get_singleton()->push_call( nc->node, diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 83ec9f819b..de9f82dadc 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -78,9 +78,9 @@ float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p return 0; } - inputs[p_input].last_pass = state->last_pass; + inputs.write[p_input].last_pass = state->last_pass; - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs[p_input].activity); + return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); } float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { @@ -221,7 +221,7 @@ StringName AnimationNode::get_input_connection(int p_input) { void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { ERR_FAIL_INDEX(p_input, inputs.size()); - inputs[p_input].connected_to = p_connection; + inputs.write[p_input].connected_to = p_connection; } String AnimationNode::get_caption() const { @@ -248,7 +248,7 @@ void AnimationNode::add_input(const String &p_name) { void AnimationNode::set_input_name(int p_input, const String &p_name) { ERR_FAIL_INDEX(p_input, inputs.size()); ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); - inputs[p_input].name = p_name; + inputs.write[p_input].name = p_name; emit_changed(); } @@ -877,7 +877,7 @@ void AnimationTree::_process_graph(float p_delta) { continue; t->loc = t->loc.linear_interpolate(loc, blend); - if (t->rot_blend_accum==0) { + if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; } else { @@ -1052,7 +1052,7 @@ void AnimationTree::_process_graph(float p_delta) { float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; if (len > t->len) { - stop=true; + stop = true; } } @@ -1065,7 +1065,7 @@ void AnimationTree::_process_graph(float p_delta) { } } - float db = Math::linear2db(MAX(blend,0.00001)); + float db = Math::linear2db(MAX(blend, 0.00001)); if (t->object->has_method("set_unit_db")) { t->object->call("set_unit_db", db); } else { @@ -1310,16 +1310,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); - - ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player",PROPERTY_HINT_NODE_PATH_VALID_TYPES,"AnimationPlayer"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); ADD_GROUP("Root Motion", "root_motion_"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track"); + + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); } AnimationTree::AnimationTree() { diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 026215508b..179f5d9698 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -1152,7 +1152,7 @@ void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringNam GET_NODE(NODE_TRANSITION, TransitionNode); ERR_FAIL_INDEX(p_input, n->input_data.size()); - n->input_data[p_input].auto_advance = p_auto_advance; + n->input_data.write[p_input].auto_advance = p_auto_advance; } void AnimationTreePlayer::transition_node_set_xfade_time(const StringName &p_node, float p_time) { @@ -1365,7 +1365,7 @@ void AnimationTreePlayer::remove_node(const StringName &p_node) { for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } @@ -1426,11 +1426,11 @@ Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const Str for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_src_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } - dst->inputs[p_dst_input].node = p_src_node; + dst->inputs.write[p_dst_input].node = p_src_node; _clear_cycle_test(); @@ -1463,7 +1463,7 @@ void AnimationTreePlayer::disconnect_nodes(const StringName &p_node, int p_input NodeBase *dst = node_map[p_node]; ERR_FAIL_INDEX(p_input, dst->inputs.size()); - dst->inputs[p_input].node = StringName(); + dst->inputs.write[p_input].node = StringName(); last_error = CONNECT_INCOMPLETE; dirty_caches = true; } @@ -1703,7 +1703,7 @@ Error AnimationTreePlayer::node_rename(const StringName &p_node, const StringNam for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) { - nb->inputs[i].node = p_new_name; + nb->inputs.write[i].node = p_new_name; } } } diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp new file mode 100644 index 0000000000..4991cedfab --- /dev/null +++ b/scene/animation/skeleton_ik.cpp @@ -0,0 +1,551 @@ +/*************************************************************************/ +/* skeleton_ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik.h" + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = childs.size() - 1; 0 <= i; --i) { + if (p_bone_id == childs[i].bone) { + return &childs.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = childs.size(); + childs.resize(infant_child_id + 1); + childs.write[infant_child_id].bone = p_bone_id; + childs.write[infant_child_id].parent_item = this; + return &childs.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND(-1 == p_task->root_bone); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector<BoneId> chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND(p_task->root_bone >= ee->tip_bone); + ERR_FAIL_INDEX(ee->tip_bone, p_task->skeleton->get_bone_count()); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } +} + +void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->childs.write[i]); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->childs.empty()) { + + ChainItem &child(sub_chain_root->childs.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + build_chain(task); + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->childs.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + + if (!ci->childs.empty()) + ci = &ci->childs.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to<Skeleton>(get_parent()); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK::SkeletonIK() : + Node(), + interpolation(1), + skeleton(NULL), + target_node_override(NULL), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + task(NULL) { + + set_process_priority(1); +} + +SkeletonIK::~SkeletonIK() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK::get_root_bone() const { + return root_bone; +} + +void SkeletonIK::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK::get_interpolation() const { + return interpolation; +} + +void SkeletonIK::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK::get_target_transform() const { + return target; +} + +void SkeletonIK::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK::is_running() { + return is_processing_internal(); +} + +void SkeletonIK::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK::stop() { + set_process_internal(false); +} + +Transform SkeletonIK::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + task->max_iterations = max_iterations; + task->min_distance = min_distance; +} + +void SkeletonIK::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); +} diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h new file mode 100644 index 0000000000..366c599c01 --- /dev/null +++ b/scene/animation/skeleton_ik.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* skeleton_ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector<ChainItem> childs; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector<ChainTip> tips; + Vector3 magnet_position; + }; + +public: + struct Task : public RID_Data { + RID self; + Skeleton *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector<EndEffector> end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static void build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK : public Node { + GDCLASS(SkeletonIK, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton *skeleton; + Spatial *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_notification); + +public: + SkeletonIK(); + virtual ~SkeletonIK(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_constraint); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // SKELETON_IK_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 9f7503577b..81fdc32788 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -150,7 +150,7 @@ void Tween::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - if (!processing) { + if (!is_active()) { //make sure that a previous process state was not saved //only process if "processing" is set set_physics_process_internal(false); @@ -164,7 +164,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_PHYSICS) break; - if (processing) + if (is_active()) _tween_process(get_process_delta_time()); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -172,7 +172,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_IDLE) break; - if (processing) + if (is_active()) _tween_process(get_physics_process_delta_time()); } break; case NOTIFICATION_EXIT_TREE: { @@ -201,7 +201,6 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all); ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL("")); ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all); - ClassDB::bind_method(D_METHOD("is_stopped"), &Tween::is_stopped); ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL("")); ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all); ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL("")); @@ -522,8 +521,8 @@ void Tween::_tween_process(float p_delta) { pending_update++; // if repeat and all interpolates was finished then reset all interpolates + bool all_finished = true; if (repeat) { - bool all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -539,9 +538,12 @@ void Tween::_tween_process(float p_delta) { reset_all(); } + all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { InterpolateData &data = E->get(); + all_finished = all_finished && data.finish; + if (!data.active || data.finish) continue; @@ -555,8 +557,8 @@ void Tween::_tween_process(float p_delta) { continue; else if (prev_delaying) { - emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); _apply_tween_value(data, data.initial_val); + emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); } if (data.elapsed > (data.delay + data.duration)) { @@ -603,32 +605,29 @@ void Tween::_tween_process(float p_delta) { } } else { Variant result = _run_equation(data); - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); _apply_tween_value(data, result); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); } if (data.finish) { _apply_tween_value(data, data.final_val); + data.elapsed = 0; emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) call_deferred("_remove", object, NodePath(Vector<StringName>(), data.key, false), true); - } + } else if (!repeat) + all_finished = all_finished && data.finish; } pending_update--; + + if (all_finished) + set_active(false); } void Tween::set_tween_process_mode(TweenProcessMode p_mode) { - if (tween_process_mode == p_mode) - return; - - bool pr = processing; - if (pr) - _set_process(false); tween_process_mode = p_mode; - if (pr) - _set_process(true); } Tween::TweenProcessMode Tween::get_tween_process_mode() const { @@ -636,32 +635,21 @@ Tween::TweenProcessMode Tween::get_tween_process_mode() const { return tween_process_mode; } -void Tween::_set_process(bool p_process, bool p_force) { - - if (processing == p_process && !p_force) - return; - - switch (tween_process_mode) { - - case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; - case TWEEN_PROCESS_IDLE: set_process_internal(p_process && active); break; - } - - processing = p_process; -} - bool Tween::is_active() const { - return active; + return is_processing_internal() || is_physics_processing_internal(); } void Tween::set_active(bool p_active) { - if (active == p_active) + if (is_active() == p_active) return; - active = p_active; - _set_process(processing, true); + switch (tween_process_mode) { + + case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break; + case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break; + } } bool Tween::is_repeat() const { @@ -687,7 +675,6 @@ float Tween::get_speed_scale() const { bool Tween::start() { set_active(true); - _set_process(true); return true; } @@ -744,14 +731,9 @@ bool Tween::stop(Object *p_object, StringName p_key) { return true; } -bool Tween::is_stopped() const { - return tell() >= get_runtime(); -} - bool Tween::stop_all() { set_active(false); - _set_process(false); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -766,7 +748,6 @@ bool Tween::stop_all() { bool Tween::resume(Object *p_object, StringName p_key) { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -785,7 +766,6 @@ bool Tween::resume(Object *p_object, StringName p_key) { bool Tween::resume_all() { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -834,7 +814,6 @@ bool Tween::remove_all() { return true; } set_active(false); - _set_process(false); interpolates.clear(); return true; } @@ -1425,8 +1404,6 @@ Tween::Tween() { //String autoplay; tween_process_mode = TWEEN_PROCESS_IDLE; - processing = false; - active = false; repeat = false; speed_scale = 1; pending_update = 0; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 36094bf294..9997349c64 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -104,8 +104,6 @@ private: String autoplay; TweenProcessMode tween_process_mode; - bool processing; - bool active; bool repeat; float speed_scale; mutable int pending_update; @@ -133,7 +131,6 @@ private: bool _apply_tween_value(InterpolateData &p_data, Variant &value); void _tween_process(float p_delta); - void _set_process(bool p_process, bool p_force = false); void _remove(Object *p_object, StringName p_key, bool first_only); protected: @@ -162,7 +159,6 @@ public: bool reset_all(); bool stop(Object *p_object, StringName p_key); bool stop_all(); - bool is_stopped() const; bool resume(Object *p_object, StringName p_key); bool resume_all(); bool remove(Object *p_object, StringName p_key); diff --git a/scene/audio/audio_player.cpp b/scene/audio/audio_player.cpp index 40ec04e479..863b278b62 100644 --- a/scene/audio/audio_player.cpp +++ b/scene/audio/audio_player.cpp @@ -41,30 +41,22 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { int buffer_size = mix_buffer.size(); if (p_fadeout) { - buffer_size = MIN(buffer_size, 16); //short fadeout ramp + // Short fadeout ramp + buffer_size = MIN(buffer_size, 128); } - // Mix if we're not paused or we're fading out - if (!stream_paused || stream_paused_fade > 0.f) { - stream_playback->mix(buffer, pitch_scale, buffer_size); - } + stream_playback->mix(buffer, pitch_scale, buffer_size); //multiply volume interpolating to avoid clicks if this changes float target_volume = p_fadeout ? -80.0 : volume_db; float vol = Math::db2linear(mix_volume_db); float vol_inc = (Math::db2linear(target_volume) - vol) / float(buffer_size); - if (stream_paused) { - vol = vol * stream_paused_fade; - if (stream_paused_fade > 0.f) { - stream_paused_fade -= 0.1f; - } - } - for (int i = 0; i < buffer_size; i++) { buffer[i] *= vol; vol += vol_inc; } + //set volume for next mix mix_volume_db = target_volume; @@ -99,8 +91,14 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { void AudioStreamPlayer::_mix_audio() { - if (!stream_playback.is_valid() || !active || - (stream_paused && stream_paused_fade <= 0.f)) { + if (!stream_playback.is_valid() || !active) + return; + + if (stream_paused) { + if (stream_paused_fade) { + _mix_internal(true); + stream_paused_fade = false; + } return; } @@ -295,7 +293,7 @@ void AudioStreamPlayer::set_stream_paused(bool p_pause) { if (p_pause != stream_paused) { stream_paused = p_pause; - stream_paused_fade = stream_paused ? 1.f : 0.f; + stream_paused_fade = p_pause ? true : false; } } @@ -385,7 +383,7 @@ AudioStreamPlayer::AudioStreamPlayer() { setseek = -1; active = false; stream_paused = false; - stream_paused_fade = 0.f; + stream_paused_fade = false; mix_target = MIX_TARGET_STEREO; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); diff --git a/scene/audio/audio_player.h b/scene/audio/audio_player.h index c42f191589..591c00ed18 100644 --- a/scene/audio/audio_player.h +++ b/scene/audio/audio_player.h @@ -56,9 +56,9 @@ private: float mix_volume_db; float pitch_scale; float volume_db; - float stream_paused_fade; bool autoplay; bool stream_paused; + bool stream_paused_fade; StringName bus; MixTarget mix_target; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index f8c188d33d..8e232c6f46 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -743,8 +743,9 @@ ColorPicker *ColorPickerButton::get_picker() { return picker; } -PopupPanel *ColorPickerButton::get_popup() const { +PopupPanel *ColorPickerButton::get_popup() { + _update_picker(); return popup; } diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index c8d8e1aa8a..0166da7118 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -144,7 +144,7 @@ public: bool is_editing_alpha() const; ColorPicker *get_picker(); - PopupPanel *get_popup() const; + PopupPanel *get_popup(); ColorPickerButton(); }; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 068af42260..18f06eca31 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -663,6 +663,9 @@ void Control::_notification(int p_notification) { bool Control::clips_input() const { + if (get_script_instance()) { + return get_script_instance()->call(SceneStringNames::get_singleton()->_clips_input); + } return false; } bool Control::has_point(const Point2 &p_point) const { @@ -2188,10 +2191,17 @@ void Control::set_tooltip(const String &p_tooltip) { data.tooltip = p_tooltip; } + String Control::get_tooltip(const Point2 &p_pos) const { return data.tooltip; } +Control *Control::make_custom_tooltip(const String &p_text) const { + if (get_script_instance()) { + return const_cast<Control *>(this)->call("_make_custom_tooltip", p_text); + } + return NULL; +} void Control::set_default_cursor_shape(CursorShape p_shape) { @@ -2820,6 +2830,8 @@ void Control::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::OBJECT, "get_drag_data", PropertyInfo(Variant::VECTOR2, "position"))); BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); BIND_VMETHOD(MethodInfo("drop_data", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::NIL, "data"))); + BIND_VMETHOD(MethodInfo(Variant::OBJECT, "_make_custom_tooltip", PropertyInfo(Variant::STRING, "for_text"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_clips_input")); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT); @@ -2964,7 +2976,6 @@ Control::Control() { data.SI = NULL; data.MI = NULL; data.RI = NULL; - data.modal = false; data.theme_owner = NULL; data.modal_exclusive = false; data.default_cursor = CURSOR_ARROW; diff --git a/scene/gui/control.h b/scene/gui/control.h index fa5274d854..6bea04345b 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -182,7 +182,6 @@ private: Control *parent; ObjectID drag_owner; - bool modal; bool modal_exclusive; uint64_t modal_frame; //frame used to put something as modal Ref<Theme> theme; @@ -454,6 +453,7 @@ public: void set_tooltip(const String &p_tooltip); virtual String get_tooltip(const Point2 &p_pos) const; + virtual Control *make_custom_tooltip(const String &p_text) const; /* CURSOR */ diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index b5622604e2..749efe8364 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -256,7 +256,7 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { if (!valid) return; - points[grabbed].offset = newofs; + points.write[grabbed].offset = newofs; points.sort(); for (int i = 0; i < points.size(); i++) { @@ -407,7 +407,7 @@ void GradientEdit::_color_changed(const Color &p_color) { if (grabbed == -1) return; - points[grabbed].color = p_color; + points.write[grabbed].color = p_color; update(); emit_signal("ramp_changed"); } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index e2c730a56e..d95ec9e495 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -268,6 +268,10 @@ void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal"); + port_grab_distance_vertical = get_constant("port_grab_distance_vertical"); + } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); Size2 vmin = v_scroll->get_combined_minimum_size(); @@ -343,8 +347,6 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { Ref<Texture> port = get_icon("port", "GraphNode"); - float grab_r_extend = 2.0; - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -354,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) + if (create_hot_zone(pos).has_point(p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (pos.distance_to(p_point) < grab_r) { + if (create_hot_zone(pos).has_point(p_point)) { return true; } } @@ -372,13 +374,11 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { - float grab_r_extend = 2.0; Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos(mb->get_position().x, mb->get_position().y); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,8 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - - if (pos.distance_to(mpos) < grab_r) { + if (create_hot_zone(pos).has_point(mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -492,7 +491,6 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Ref<Texture> port = get_icon("port", "GraphNode"); Vector2 mpos = mm->get_position(); - float grab_r = port->get_width() * 0.5 * grab_r_extend; for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); @@ -504,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; @@ -519,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && pos.distance_to(mpos) < grab_r) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -559,6 +557,10 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } +Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { + return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +} + template <class Vector2> static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, Vector2 start, Vector2 control_1, Vector2 control_2, Vector2 end) { /* Formula from Wikipedia article on Bezier curves. */ diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 14789001e4..64ba18681e 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -81,6 +81,9 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; + float port_grab_distance_horizontal; + float port_grab_distance_vertical; + bool connecting; String connecting_from; bool connecting_out; @@ -127,6 +130,9 @@ private: Control *connections_layer; GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); + + Rect2 create_hot_zone(const Vector2 &pos); + void _top_layer_draw(); void _connections_layer_draw(); void _update_scroll_offset(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 72ed0e9b81..5c79741682 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -72,7 +72,7 @@ void ItemList::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].text = p_text; + items.write[p_idx].text = p_text; update(); shape_changed = true; } @@ -85,7 +85,7 @@ String ItemList::get_item_text(int p_idx) const { void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip_enabled = p_enabled; + items.write[p_idx].tooltip_enabled = p_enabled; } bool ItemList::is_item_tooltip_enabled(int p_idx) const { @@ -97,7 +97,7 @@ void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip = p_tooltip; + items.write[p_idx].tooltip = p_tooltip; update(); shape_changed = true; } @@ -112,7 +112,7 @@ void ItemList::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon = p_icon; + items.write[p_idx].icon = p_icon; update(); shape_changed = true; } @@ -128,7 +128,7 @@ void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon_region = p_region; + items.write[p_idx].icon_region = p_region; update(); shape_changed = true; } @@ -144,7 +144,7 @@ void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon_modulate = p_modulate; + items.write[p_idx].icon_modulate = p_modulate; update(); } @@ -159,7 +159,7 @@ void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_colo ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].custom_bg = p_custom_bg_color; + items.write[p_idx].custom_bg = p_custom_bg_color; } Color ItemList::get_item_custom_bg_color(int p_idx) const { @@ -173,7 +173,7 @@ void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_colo ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].custom_fg = p_custom_fg_color; + items.write[p_idx].custom_fg = p_custom_fg_color; } Color ItemList::get_item_custom_fg_color(int p_idx) const { @@ -187,7 +187,7 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture> &p_tag_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tag_icon = p_tag_icon; + items.write[p_idx].tag_icon = p_tag_icon; update(); shape_changed = true; } @@ -202,7 +202,7 @@ void ItemList::set_item_selectable(int p_idx, bool p_selectable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].selectable = p_selectable; + items.write[p_idx].selectable = p_selectable; } bool ItemList::is_item_selectable(int p_idx) const { @@ -215,7 +215,7 @@ void ItemList::set_item_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].disabled = p_disabled; + items.write[p_idx].disabled = p_disabled; update(); } @@ -229,7 +229,7 @@ void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].metadata = p_metadata; + items.write[p_idx].metadata = p_metadata; update(); shape_changed = true; } @@ -250,7 +250,7 @@ void ItemList::select(int p_idx, bool p_single) { } for (int i = 0; i < items.size(); i++) { - items[i].selected = p_idx == i; + items.write[i].selected = p_idx == i; } current = p_idx; @@ -258,7 +258,7 @@ void ItemList::select(int p_idx, bool p_single) { } else { if (items[p_idx].selectable && !items[p_idx].disabled) { - items[p_idx].selected = true; + items.write[p_idx].selected = true; } } update(); @@ -268,10 +268,10 @@ void ItemList::unselect(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); if (select_mode != SELECT_MULTI) { - items[p_idx].selected = false; + items.write[p_idx].selected = false; current = -1; } else { - items[p_idx].selected = false; + items.write[p_idx].selected = false; } update(); } @@ -283,9 +283,9 @@ void ItemList::unselect_all() { for (int i = 0; i < items.size(); i++) { - items[i].selected = false; + items.write[i].selected = false; } - + current = -1; update(); } @@ -869,8 +869,8 @@ void ItemList::_notification(int p_what) { // elements need to adapt to the selected size minsize.y += vseparation; minsize.x += hseparation; - items[i].rect_cache.size = minsize; - items[i].min_rect_cache.size = minsize; + items.write[i].rect_cache.size = minsize; + items.write[i].min_rect_cache.size = minsize; } int fit_size = size.x - bg->get_minimum_size().width - mw; @@ -897,8 +897,8 @@ void ItemList::_notification(int p_what) { } if (same_column_width) - items[i].rect_cache.size.x = max_column_width; - items[i].rect_cache.position = ofs; + items.write[i].rect_cache.size.x = max_column_width; + items.write[i].rect_cache.position = ofs; max_h = MAX(max_h, items[i].rect_cache.size.y); ofs.x += items[i].rect_cache.size.x + hseparation; col++; @@ -908,7 +908,7 @@ void ItemList::_notification(int p_what) { separators.push_back(ofs.y + max_h + vseparation / 2); for (int j = i; j >= 0 && col > 0; j--, col--) { - items[j].rect_cache.size.y = max_h; + items.write[j].rect_cache.size.y = max_h; } ofs.x = 0; @@ -919,7 +919,7 @@ void ItemList::_notification(int p_what) { } for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { - items[j].rect_cache.size.y = max_h; + items.write[j].rect_cache.size.y = max_h; } if (all_fit) { @@ -1103,8 +1103,8 @@ void ItemList::_notification(int p_what) { int cs = j < ss ? font->get_char_size(items[i].text[j], items[i].text[j + 1]).x : 0; if (ofs + cs > max_len || j == ss) { - line_limit_cache[line] = j; - line_size_cache[line] = ofs; + line_limit_cache.write[line] = j; + line_size_cache.write[line] = ofs; line++; ofs = 0; if (line >= max_text_lines) diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 9af479c1cc..0b36e1663c 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -75,7 +75,7 @@ void Label::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { - if (clip || autowrap) { + if (clip) { VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index b71a4dd133..6258f32bf3 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -35,7 +35,9 @@ #include "os/os.h" #include "print_string.h" #include "translation.h" + #ifdef TOOLS_ENABLED +#include "editor/editor_scale.h" #include "editor/editor_settings.h" #endif @@ -640,7 +642,7 @@ void LineEdit::_notification(int p_what) { int char_ofs = window_pos; int y_area = height - style->get_minimum_size().height; - int y_ofs = style->get_offset().y; + int y_ofs = style->get_offset().y + (y_area - font->get_height()) / 2; int font_ascent = font->get_ascent(); @@ -716,7 +718,11 @@ void LineEdit::_notification(int p_what) { if (char_ofs == cursor_pos && draw_caret) { if (ime_text.length() == 0) { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); +#else VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); +#endif } } @@ -755,7 +761,11 @@ void LineEdit::_notification(int p_what) { if (char_ofs == cursor_pos && draw_caret) { //may be at the end if (ime_text.length() == 0) { +#ifdef TOOLS_ENABLED + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); +#else VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(1, caret_height)), cursor_color); +#endif } } diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 2356444ecb..0636accfee 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -43,7 +43,6 @@ class MenuButton : public Button { bool clicked; bool disable_shortcuts; PopupMenu *popup; - virtual void pressed(); void _unhandled_key_input(Ref<InputEvent> p_event); Array _get_items() const; @@ -55,6 +54,8 @@ protected: static void _bind_methods(); public: + virtual void pressed(); + PopupMenu *get_popup() const; void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index c5e4149782..2901176a69 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -36,7 +36,7 @@ Size2 OptionButton::get_minimum_size() const { Size2 minsize = Button::get_minimum_size(); if (has_icon("arrow")) - minsize.width += Control::get_icon("arrow")->get_width(); + minsize.width += Control::get_icon("arrow")->get_width() + get_constant("hseparation"); return minsize; } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 18e609c798..ab762e19ee 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -398,10 +398,19 @@ void PopupMenu::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + + PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent()); + if (pm) { + // Inherit submenu's popup delay time from parent menu + float pm_delay = pm->get_submenu_popup_delay(); + set_submenu_popup_delay(pm_delay); + } + } break; case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < items.size(); i++) { - items[i].xl_text = tr(items[i].text); + items.write[i].xl_text = tr(items[i].text); } minimum_size_changed(); @@ -421,6 +430,8 @@ void PopupMenu::_notification(int p_what) { Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") }; Ref<Texture> submenu = get_icon("submenu"); Ref<StyleBox> separator = get_stylebox("separator"); + Ref<StyleBox> labeled_separator_left = get_stylebox("labeled_separator_left"); + Ref<StyleBox> labeled_separator_right = get_stylebox("labeled_separator_right"); style->draw(ci, Rect2(Point2(), get_size())); Point2 ofs = style->get_offset(); @@ -457,10 +468,25 @@ void PopupMenu::_notification(int p_what) { hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation))); } + String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; + if (items[i].separator) { int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; - separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); + if (text != String()) { + int ss = font->get_string_size(text).width; + int center = (get_size().width) / 2; + int l = center - ss / 2; + int r = center + ss / 2; + if (l > item_ofs.x) { + labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h))); + } + if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) { + labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h))); + } + } else { + separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h))); + } } if (items[i].checkable_type) { @@ -480,8 +506,13 @@ void PopupMenu::_notification(int p_what) { } item_ofs.y += font->get_ascent(); - String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].xl_text; - if (!items[i].separator) { + if (items[i].separator) { + + if (text != String()) { + int center = (get_size().width - font->get_string_size(text).width) / 2; + font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled); + } + } else { font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color)); } @@ -493,12 +524,17 @@ void PopupMenu::_notification(int p_what) { font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, i == mouse_over ? font_color_hover : font_color_accel); } - items[i]._ofs_cache = ofs.y; + items.write[i]._ofs_cache = ofs.y; ofs.y += h; } } break; + case MainLoop::NOTIFICATION_WM_FOCUS_OUT: { + + if (hide_on_window_lose_focus) + hide(); + } break; case NOTIFICATION_MOUSE_ENTER: { grab_focus(); @@ -591,7 +627,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) { add_check_item(p_label, p_ID, p_accel); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -599,7 +635,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) { add_icon_check_item(p_icon, p_label, p_ID, p_accel); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -671,7 +707,7 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) { add_check_shortcut(p_shortcut, p_ID, p_global); - items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; + items.write[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; update(); minimum_size_changed(); } @@ -693,8 +729,8 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int void PopupMenu::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].text = p_text; - items[p_idx].xl_text = tr(p_text); + items.write[p_idx].text = p_text; + items.write[p_idx].xl_text = tr(p_text); update(); minimum_size_changed(); @@ -702,7 +738,7 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { void PopupMenu::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].icon = p_icon; + items.write[p_idx].icon = p_icon; update(); minimum_size_changed(); @@ -711,7 +747,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checked = p_checked; + items.write[p_idx].checked = p_checked; update(); minimum_size_changed(); @@ -719,7 +755,7 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { void PopupMenu::set_item_id(int p_idx, int p_ID) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].ID = p_ID; + items.write[p_idx].ID = p_ID; update(); minimum_size_changed(); @@ -728,7 +764,7 @@ void PopupMenu::set_item_id(int p_idx, int p_ID) { void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].accel = p_accel; + items.write[p_idx].accel = p_accel; update(); minimum_size_changed(); @@ -737,7 +773,7 @@ void PopupMenu::set_item_accelerator(int p_idx, uint32_t p_accel) { void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].metadata = p_meta; + items.write[p_idx].metadata = p_meta; update(); minimum_size_changed(); } @@ -745,7 +781,7 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].disabled = p_disabled; + items.write[p_idx].disabled = p_disabled; update(); minimum_size_changed(); } @@ -753,7 +789,7 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].submenu = p_submenu; + items.write[p_idx].submenu = p_submenu; update(); minimum_size_changed(); } @@ -761,7 +797,7 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checked = !items[p_idx].checked; + items.write[p_idx].checked = !items[p_idx].checked; update(); minimum_size_changed(); } @@ -855,7 +891,7 @@ int PopupMenu::get_item_state(int p_idx) const { void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].separator = p_separator; + items.write[p_idx].separator = p_separator; update(); } @@ -867,21 +903,21 @@ bool PopupMenu::is_item_separator(int p_idx) const { void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; update(); } void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; + items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; update(); } void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].tooltip = p_tooltip; + items.write[p_idx].tooltip = p_tooltip; update(); } @@ -890,8 +926,8 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo if (items[p_idx].shortcut.is_valid()) { _unref_shortcut(items[p_idx].shortcut); } - items[p_idx].shortcut = p_shortcut; - items[p_idx].shortcut_is_global = p_global; + items.write[p_idx].shortcut = p_shortcut; + items.write[p_idx].shortcut_is_global = p_global; if (items[p_idx].shortcut.is_valid()) { _ref_shortcut(items[p_idx].shortcut); @@ -903,7 +939,7 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bo void PopupMenu::set_item_h_offset(int p_idx, int p_offset) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].h_ofs = p_offset; + items.write[p_idx].h_ofs = p_offset; update(); minimum_size_changed(); } @@ -911,14 +947,14 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) { void PopupMenu::set_item_multistate(int p_idx, int p_state) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].state = p_state; + items.write[p_idx].state = p_state; update(); } void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, items.size()); - items[p_idx].shortcut_is_disabled = p_disabled; + items.write[p_idx].shortcut_is_disabled = p_disabled; update(); } @@ -929,9 +965,9 @@ void PopupMenu::toggle_item_multistate(int p_idx) { return; } - ++items[p_idx].state; - if (items[p_idx].max_states <= items[p_idx].state) - items[p_idx].state = 0; + ++items.write[p_idx].state; + if (items.write[p_idx].max_states <= items[p_idx].state) + items.write[p_idx].state = 0; update(); } @@ -1064,11 +1100,15 @@ void PopupMenu::remove_item(int p_idx) { update(); } -void PopupMenu::add_separator() { +void PopupMenu::add_separator(const String &p_text) { Item sep; sep.separator = true; sep.ID = -1; + if (p_text != String()) { + sep.text = p_text; + sep.xl_text = tr(p_text); + } items.push_back(sep); update(); } @@ -1201,6 +1241,29 @@ bool PopupMenu::is_hide_on_multistate_item_selection() const { return hide_on_multistate_item_selection; } +void PopupMenu::set_submenu_popup_delay(float p_time) { + + if (p_time <= 0) + p_time = 0.01; + + submenu_timer->set_wait_time(p_time); +} + +float PopupMenu::get_submenu_popup_delay() const { + + return submenu_timer->get_wait_time(); +} + +void PopupMenu::set_hide_on_window_lose_focus(bool p_enabled) { + + hide_on_window_lose_focus = p_enabled; +} + +bool PopupMenu::is_hide_on_window_lose_focus() const { + + return hide_on_window_lose_focus; +} + String PopupMenu::get_tooltip(const Point2 &p_pos) const { int over = _get_mouse_over(p_pos); @@ -1288,7 +1351,7 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_item", "idx"), &PopupMenu::remove_item); - ClassDB::bind_method(D_METHOD("add_separator"), &PopupMenu::add_separator); + ClassDB::bind_method(D_METHOD("add_separator", "label"), &PopupMenu::add_separator, DEFVAL(String())); ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear); ClassDB::bind_method(D_METHOD("_set_items"), &PopupMenu::_set_items); @@ -1303,12 +1366,19 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection); ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection); + ClassDB::bind_method(D_METHOD("set_submenu_popup_delay", "seconds"), &PopupMenu::set_submenu_popup_delay); + ClassDB::bind_method(D_METHOD("get_submenu_popup_delay"), &PopupMenu::get_submenu_popup_delay); + + ClassDB::bind_method(D_METHOD("set_hide_on_window_lose_focus", "enable"), &PopupMenu::set_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("is_hide_on_window_lose_focus"), &PopupMenu::is_hide_on_window_lose_focus); + ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay"); ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID"))); ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID"))); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index d3ee9be1c0..a06a17c9fe 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -101,6 +101,7 @@ class PopupMenu : public Popup { bool hide_on_item_selection; bool hide_on_checkable_item_selection; bool hide_on_multistate_item_selection; + bool hide_on_window_lose_focus; Vector2 moved; Array _get_items() const; @@ -180,7 +181,7 @@ public: void remove_item(int p_idx); - void add_separator(); + void add_separator(const String &p_text = String()); void clear(); @@ -202,8 +203,14 @@ public: void set_hide_on_multistate_item_selection(bool p_enabled); bool is_hide_on_multistate_item_selection() const; + void set_submenu_popup_delay(float p_time); + float get_submenu_popup_delay() const; + virtual void popup(const Rect2 &p_bounds = Rect2()); + void set_hide_on_window_lose_focus(bool p_enabled); + bool is_hide_on_window_lose_focus() const; + PopupMenu(); ~PopupMenu(); }; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ce2e3538da..a3748bf14c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -149,7 +149,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (r_click_item) *r_click_item = NULL; } - Line &l = p_frame->lines[p_line]; + Line &l = p_frame->lines.write[p_line]; Item *it = l.from; int line_ofs = 0; @@ -535,9 +535,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int idx = 0; //set minimums to zero for (int i = 0; i < table->columns.size(); i++) { - table->columns[i].min_width = 0; - table->columns[i].max_width = 0; - table->columns[i].width = 0; + table->columns.write[i].min_width = 0; + table->columns.write[i].max_width = 0; + table->columns.write[i].width = 0; } //compute minimum width for each cell const int available_width = p_width - hseparation * (table->columns.size() - 1) - wofs; @@ -553,8 +553,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < frame->lines.size(); i++) { _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); - table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); - table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); + table->columns.write[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns.write[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } idx++; } @@ -568,16 +568,16 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < table->columns.size(); i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) - table->columns[i].expand = true; + table->columns.write[i].expand = true; if (table->columns[i].expand) total_ratio += table->columns[i].expand_ratio; } //assign actual widths for (int i = 0; i < table->columns.size(); i++) { - table->columns[i].width = table->columns[i].min_width; + table->columns.write[i].width = table->columns[i].min_width; if (table->columns[i].expand) - table->columns[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; + table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; table->total_width += table->columns[i].width + hseparation; } @@ -592,7 +592,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int dif = table->columns[i].width - table->columns[i].max_width; if (dif > 0) { table_need_fit = true; - table->columns[i].width = table->columns[i].max_width; + table->columns.write[i].width = table->columns[i].max_width; table->total_width -= dif; total_ratio -= table->columns[i].expand_ratio; } @@ -606,7 +606,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & if (dif > 0) { int slice = table->columns[i].expand_ratio * remaining_width / total_ratio; int incr = MIN(dif, slice); - table->columns[i].width += incr; + table->columns.write[i].width += incr; table->total_width += incr; } } @@ -626,8 +626,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int ly = 0; _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs); - frame->lines[i].height_cache = ly; //actual height - frame->lines[i].height_accum_cache = ly; //actual height + frame->lines.write[i].height_cache = ly; //actual height + frame->lines.write[i].height_accum_cache = ly; //actual height } idx++; } @@ -669,7 +669,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & yofs += frame->lines[i].height_cache; if (p_mode == PROCESS_CACHE) { - frame->lines[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; + frame->lines.write[i].height_accum_cache = offset.y + draw_ofs.y + frame->lines[i].height_cache; } } @@ -1253,6 +1253,9 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { //validate invalid lines Size2 size = get_size(); + if (fixed_width != -1) { + size.width = fixed_width; + } Rect2 text_rect = _get_text_rect(); Color font_color_shadow = get_color("font_color_shadow"); bool use_outline = get_constant("shadow_as_outline"); @@ -1264,11 +1267,11 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { int y = 0; _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs); - p_frame->lines[i].height_cache = y; - p_frame->lines[i].height_accum_cache = y; + p_frame->lines.write[i].height_cache = y; + p_frame->lines.write[i].height_accum_cache = y; if (i > 0) - p_frame->lines[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache; + p_frame->lines.write[i].height_accum_cache += p_frame->lines[i - 1].height_accum_cache; } int total_height = 0; @@ -1343,7 +1346,7 @@ void RichTextLabel::add_text(const String &p_text) { _add_item(item, false); current_frame->lines.resize(current_frame->lines.size() + 1); if (item->type != ITEM_NEWLINE) - current_frame->lines[current_frame->lines.size() - 1].from = item; + current_frame->lines.write[current_frame->lines.size() - 1].from = item; _invalidate_current_line(current_frame); } @@ -1366,7 +1369,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) } if (current_frame->lines[current_frame->lines.size() - 1].from == NULL) { - current_frame->lines[current_frame->lines.size() - 1].from = p_item; + current_frame->lines.write[current_frame->lines.size() - 1].from = p_item; } p_item->line = current_frame->lines.size() - 1; @@ -1428,7 +1431,7 @@ bool RichTextLabel::remove_line(const int p_line) { _remove_item(current->subitems[lines], current->subitems[lines]->line, lines); if (p_line == 0) { - main->lines[0].from = main; + main->lines.write[0].from = main; } main->first_invalid_line = 0; @@ -1507,8 +1510,8 @@ void RichTextLabel::push_table(int p_columns) { item->columns.resize(p_columns); item->total_width = 0; for (int i = 0; i < item->columns.size(); i++) { - item->columns[i].expand = false; - item->columns[i].expand_ratio = 1; + item->columns.write[i].expand = false; + item->columns.write[i].expand_ratio = 1; } _add_item(item, true, true); } @@ -1518,8 +1521,8 @@ void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_r ERR_FAIL_COND(current->type != ITEM_TABLE); ItemTable *table = static_cast<ItemTable *>(current); ERR_FAIL_INDEX(p_column, table->columns.size()); - table->columns[p_column].expand = p_expand; - table->columns[p_column].expand_ratio = p_ratio; + table->columns.write[p_column].expand = p_expand; + table->columns.write[p_column].expand_ratio = p_ratio; } void RichTextLabel::push_cell() { @@ -1533,7 +1536,7 @@ void RichTextLabel::push_cell() { item->cell = true; item->parent_line = item->parent_frame->lines.size() - 1; item->lines.resize(1); - item->lines[0].from = NULL; + item->lines.write[0].from = NULL; item->first_invalid_line = 0; } @@ -2245,13 +2248,28 @@ int RichTextLabel::get_total_character_count() const { return tc; } +void RichTextLabel::set_fixed_size_to_width(int p_width) { + fixed_width = p_width; + minimum_size_changed(); +} + +Size2 RichTextLabel::get_minimum_size() const { + + if (fixed_width != -1) { + const_cast<RichTextLabel *>(this)->_validate_line_caches(main); + return Size2(fixed_width, const_cast<RichTextLabel *>(this)->get_content_height()); + } + + return Size2(); +} + RichTextLabel::RichTextLabel() { main = memnew(ItemFrame); main->index = 0; current = main; main->lines.resize(1); - main->lines[0].from = main; + main->lines.write[0].from = main; main->first_invalid_line = 0; current_frame = main; tab_size = 4; @@ -2287,6 +2305,7 @@ RichTextLabel::RichTextLabel() { percent_visible = 1; visible_line_count = 0; + fixed_width = -1; set_clip_contents(true); } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index af368af46a..06e9b8efe3 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -293,6 +293,8 @@ private: void _update_all_lines(); + int fixed_width; + protected: void _notification(int p_what); @@ -368,6 +370,9 @@ public: void set_percent_visible(float p_percent); float get_percent_visible() const; + void set_fixed_size_to_width(int p_width); + virtual Size2 get_minimum_size() const; + RichTextLabel(); ~RichTextLabel(); }; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 6ec67aca6b..e5bd1c453d 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -257,9 +257,7 @@ void ScrollBar::_notification(int p_what) { Point2 ofs; - VisualServer *vs = VisualServer::get_singleton(); - - vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid()); + decr->draw(ci, Point2()); if (orientation == HORIZONTAL) ofs.x += decr->get_width(); @@ -280,7 +278,7 @@ void ScrollBar::_notification(int p_what) { else ofs.height += area.height; - vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid()); + incr->draw(ci, ofs); Rect2 grabber_rect; if (orientation == HORIZONTAL) { diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 46215c9277..b820e2eafd 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -65,11 +65,12 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { } else { grab.active = false; } - } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { - - set_value(get_value() + get_step()); - } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { - set_value(get_value() - get_step()); + } else if (scrollable) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { + set_value(get_value() + get_step()); + } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + set_value(get_value() - get_step()); + } } } @@ -247,6 +248,16 @@ bool Slider::is_editable() const { return editable; } +void Slider::set_scrollable(bool p_scrollable) { + + scrollable = p_scrollable; +} + +bool Slider::is_scrollable() const { + + return scrollable; +} + void Slider::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Slider::_gui_input); @@ -258,8 +269,11 @@ void Slider::_bind_methods() { ClassDB::bind_method(D_METHOD("set_editable", "editable"), &Slider::set_editable); ClassDB::bind_method(D_METHOD("is_editable"), &Slider::is_editable); + ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable); + ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); @@ -272,5 +286,6 @@ Slider::Slider(Orientation p_orientation) { ticks = 0; custom_step = -1; editable = true; + scrollable = true; set_focus_mode(FOCUS_ALL); } diff --git a/scene/gui/slider.h b/scene/gui/slider.h index e77a0b7423..4d02348159 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -48,6 +48,7 @@ class Slider : public Range { Orientation orientation; float custom_step; bool editable; + bool scrollable; protected: void _gui_input(Ref<InputEvent> p_event); @@ -70,6 +71,9 @@ public: void set_editable(bool p_editable); bool is_editable() const; + void set_scrollable(bool p_scrollable); + bool is_scrollable() const; + Slider(Orientation p_orientation = VERTICAL); }; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index b114264de1..2075f7ce70 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -189,7 +189,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { update(); } - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) { // clicks Point2 pos(mb->get_position().x, mb->get_position().y); @@ -286,7 +286,7 @@ void Tabs::_notification(int p_what) { for (int i = 0; i < tabs.size(); i++) { - tabs[i].ofs_cache = mw; + tabs.write[i].ofs_cache = mw; mw += get_tab_width(i); } @@ -314,7 +314,7 @@ void Tabs::_notification(int p_what) { if (i < offset) continue; - tabs[i].ofs_cache = w; + tabs.write[i].ofs_cache = w; int lsize = tabs[i].size_cache; @@ -379,7 +379,7 @@ void Tabs::_notification(int p_what) { rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP))); w += rb->get_width(); - tabs[i].rb_rect = rb_rect; + tabs.write[i].rb_rect = rb_rect; } if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { @@ -403,7 +403,7 @@ void Tabs::_notification(int p_what) { cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP))); w += cb->get_width(); - tabs[i].cb_rect = cb_rect; + tabs.write[i].cb_rect = cb_rect; } w += sb->get_margin(MARGIN_RIGHT); @@ -471,7 +471,7 @@ bool Tabs::get_offset_buttons_visible() const { void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].text = p_title; + tabs.write[p_tab].text = p_title; update(); minimum_size_changed(); } @@ -485,7 +485,7 @@ String Tabs::get_tab_title(int p_tab) const { void Tabs::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].icon = p_icon; + tabs.write[p_tab].icon = p_icon; update(); minimum_size_changed(); } @@ -499,7 +499,7 @@ Ref<Texture> Tabs::get_tab_icon(int p_tab) const { void Tabs::set_tab_disabled(int p_tab, bool p_disabled) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].disabled = p_disabled; + tabs.write[p_tab].disabled = p_disabled; update(); } bool Tabs::get_tab_disabled(int p_tab) const { @@ -511,7 +511,7 @@ bool Tabs::get_tab_disabled(int p_tab) const { void Tabs::set_tab_right_button(int p_tab, const Ref<Texture> &p_right_button) { ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs[p_tab].right_button = p_right_button; + tabs.write[p_tab].right_button = p_right_button; _update_cache(); update(); minimum_size_changed(); @@ -536,9 +536,9 @@ void Tabs::_update_cache() { int size_fixed = 0; int count_resize = 0; for (int i = 0; i < tabs.size(); i++) { - tabs[i].ofs_cache = mw; - tabs[i].size_cache = get_tab_width(i); - tabs[i].size_text = font->get_string_size(tabs[i].text).width; + tabs.write[i].ofs_cache = mw; + tabs.write[i].size_cache = get_tab_width(i); + tabs.write[i].size_text = font->get_string_size(tabs[i].text).width; mw += tabs[i].size_cache; if (tabs[i].size_cache <= min_width || i == current) { size_fixed += tabs[i].size_cache; @@ -579,9 +579,9 @@ void Tabs::_update_cache() { lsize = m_width; } } - tabs[i].ofs_cache = w; - tabs[i].size_cache = lsize; - tabs[i].size_text = slen; + tabs.write[i].ofs_cache = w; + tabs.write[i].size_cache = lsize; + tabs.write[i].size_text = slen; w += lsize; } } @@ -920,6 +920,14 @@ int Tabs::get_tabs_rearrange_group() const { return tabs_rearrange_group; } +void Tabs::set_select_with_rmb(bool p_enabled) { + select_with_rmb = p_enabled; +} + +bool Tabs::get_select_with_rmb() const { + return select_with_rmb; +} + void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); @@ -950,6 +958,9 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &Tabs::set_select_with_rmb); + ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb); + ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab"))); @@ -988,6 +999,8 @@ Tabs::Tabs() { offset = 0; max_drawn_tab = 0; + select_with_rmb = false; + min_width = 0; scrolling_enabled = true; buttons_visible = false; diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 3b38e7f2cb..e204f4364b 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -83,6 +83,8 @@ private: int rb_hover; bool rb_pressing; + bool select_with_rmb; + int cb_hover; bool cb_pressing; CloseButtonDisplayPolicy cb_displaypolicy; @@ -150,6 +152,9 @@ public: void set_tabs_rearrange_group(int p_group_id); int get_tabs_rearrange_group() const; + void set_select_with_rmb(bool p_enabled); + bool get_select_with_rmb() const; + void ensure_tab_visible(int p_idx); void set_min_width(int p_width); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 218b5060a1..d3de68ee24 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -127,13 +127,13 @@ void TextEdit::Text::_update_line_cache(int p_line) const { w += get_char_width(str[i], str[i + 1], w); } - text[p_line].width_cache = w; + text.write[p_line].width_cache = w; - text[p_line].wrap_amount_cache = -1; + text.write[p_line].wrap_amount_cache = -1; //update regions - text[p_line].region_info.clear(); + text.write[p_line].region_info.clear(); for (int i = 0; i < len; i++) { @@ -172,7 +172,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { ColorRegionInfo cri; cri.end = false; cri.region = j; - text[p_line].region_info[i] = cri; + text.write[p_line].region_info[i] = cri; i += lr - 1; break; @@ -200,7 +200,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const { ColorRegionInfo cri; cri.end = true; cri.region = j; - text[p_line].region_info[i] = cri; + text.write[p_line].region_info[i] = cri; i += lr - 1; break; @@ -236,7 +236,7 @@ void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const { ERR_FAIL_INDEX(p_line, text.size()); - text[p_line].wrap_amount_cache = p_wrap_amount; + text.write[p_line].wrap_amount_cache = p_wrap_amount; } int TextEdit::Text::get_line_wrap_amount(int p_line) const { @@ -249,14 +249,14 @@ int TextEdit::Text::get_line_wrap_amount(int p_line) const { void TextEdit::Text::clear_width_cache() { for (int i = 0; i < text.size(); i++) { - text[i].width_cache = -1; + text.write[i].width_cache = -1; } } void TextEdit::Text::clear_wrap_cache() { for (int i = 0; i < text.size(); i++) { - text[i].wrap_amount_cache = -1; + text.write[i].wrap_amount_cache = -1; } } @@ -281,15 +281,16 @@ void TextEdit::Text::set(int p_line, const String &p_text) { ERR_FAIL_INDEX(p_line, text.size()); - text[p_line].width_cache = -1; - text[p_line].wrap_amount_cache = -1; - text[p_line].data = p_text; + text.write[p_line].width_cache = -1; + text.write[p_line].wrap_amount_cache = -1; + text.write[p_line].data = p_text; } void TextEdit::Text::insert(int p_at, const String &p_text) { Line line; line.marked = false; + line.safe = false; line.breakpoint = false; line.hidden = false; line.width_cache = -1; @@ -336,10 +337,6 @@ void TextEdit::_update_scrollbars() { int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int first_vis_line = get_first_visible_line(); - int wi; - int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi)); - int total_rows = get_total_visible_rows(); if (scroll_past_end_of_file_enabled) { total_rows += visible_rows - 1; @@ -972,7 +969,7 @@ void TextEdit::_notification(int p_what) { fc = line_num_padding + fc; } - cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, cache.line_number_color); + cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color); } } @@ -1151,10 +1148,18 @@ void TextEdit::_notification(int p_what) { if (ime_text.length() == 0) { if (draw_caret) { if (insert_mode) { - int caret_h = (block_caret) ? 4 : 1; +#ifdef TOOLS_ENABLED + int caret_h = (block_caret) ? 4 : 2 * EDSCALE; +#else + int caret_h = (block_caret) ? 4 : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color); } else { - caret_w = (block_caret) ? caret_w : 1; +#ifdef TOOLS_ENABLED + caret_w = (block_caret) ? caret_w : 2 * EDSCALE; +#else + caret_w = (block_caret) ? caret_w : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); } } @@ -1227,11 +1232,19 @@ void TextEdit::_notification(int p_what) { if (draw_caret) { if (insert_mode) { int char_w = cache.font->get_char_size(' ').width; - int caret_h = (block_caret) ? 4 : 1; +#ifdef TOOLS_ENABLED + int caret_h = (block_caret) ? 4 : 2 * EDSCALE; +#else + int caret_h = (block_caret) ? 4 : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color); } else { int char_w = cache.font->get_char_size(' ').width; - int caret_w = (block_caret) ? char_w : 1; +#ifdef TOOLS_ENABLED + int caret_w = (block_caret) ? char_w : 2 * EDSCALE; +#else + int caret_w = (block_caret) ? char_w : 2; +#endif VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color); } } @@ -1672,7 +1685,6 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co rows /= get_row_height(); rows += get_v_scroll_offset(); int first_vis_line = get_first_visible_line(); - int last_vis_line = get_last_visible_line(); int row = first_vis_line + Math::floor(rows); int wrap_index = 0; @@ -3799,7 +3811,6 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const { } // line ends before hit wrap_at; add this word to the substring wrap_substring += word_str; - px += word_px; lines.push_back(wrap_substring); return lines; } @@ -4314,6 +4325,7 @@ void TextEdit::_update_caches() { cache.caret_color = get_color("caret_color"); cache.caret_background_color = get_color("caret_background_color"); cache.line_number_color = get_color("line_number_color"); + cache.safe_line_number_color = get_color("safe_line_number_color"); cache.font_color = get_color("font_color"); cache.font_selected_color = get_color("font_selected_color"); cache.keyword_color = get_color("keyword_color"); @@ -4332,7 +4344,11 @@ void TextEdit::_update_caches() { cache.search_result_border_color = get_color("search_result_border_color"); cache.symbol_color = get_color("symbol_color"); cache.background_color = get_color("background_color"); +#ifdef TOOLS_ENABLED + cache.line_spacing = get_constant("line_spacing") * EDSCALE; +#else cache.line_spacing = get_constant("line_spacing"); +#endif cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons"); @@ -4885,6 +4901,17 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) { update(); } +void TextEdit::set_line_as_safe(int p_line, bool p_safe) { + ERR_FAIL_INDEX(p_line, text.size()); + text.set_safe(p_line, p_safe); + update(); +} + +bool TextEdit::is_line_set_as_safe(int p_line) const { + ERR_FAIL_INDEX_V(p_line, text.size(), false); + return text.is_safe(p_line); +} + bool TextEdit::is_line_set_as_breakpoint(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), false); @@ -5506,9 +5533,8 @@ int TextEdit::get_last_visible_line() const { int TextEdit::get_last_visible_line_wrap_index() const { int first_vis_line = get_first_visible_line(); - int last_vis_line = 0; int wi; - last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1; + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi); return wi; } @@ -5723,7 +5749,7 @@ void TextEdit::_update_completion_candidates() { for (int i = 0; i < completion_strings.size(); i++) { if (single_quote && completion_strings[i].is_quoted()) { - completion_strings[i] = completion_strings[i].unquote().quote("'"); + completion_strings.write[i] = completion_strings[i].unquote().quote("'"); } if (s == completion_strings[i]) { @@ -6384,7 +6410,7 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int } // check for dot or underscore or 'x' for hex notation in floating point number - if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) { + if ((str[j] == '.' || str[j] == 'x' || str[j] == '_' || str[j] == 'f') && !in_word && prev_is_number && !is_number) { is_number = true; is_symbol = false; is_char = false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 586f4c8e93..19b5d574c6 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -76,6 +76,7 @@ public: bool marked : 1; bool breakpoint : 1; bool hidden : 1; + bool safe : 1; int wrap_amount_cache : 24; Map<int, ColorRegionInfo> region_info; String data; @@ -100,12 +101,14 @@ public: int get_line_wrap_amount(int p_line) const; const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const; void set(int p_line, const String &p_text); - void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; } + void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; } bool is_marked(int p_line) const { return text[p_line].marked; } - void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } + void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; } bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } - void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } + void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; } bool is_hidden(int p_line) const { return text[p_line].hidden; } + void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; } + bool is_safe(int p_line) const { return text[p_line].safe; } void insert(int p_at, const String &p_text); void remove(int p_at); int size() const { return text.size(); } @@ -165,6 +168,7 @@ private: Color caret_color; Color caret_background_color; Color line_number_color; + Color safe_line_number_color; Color font_color; Color font_selected_color; Color keyword_color; @@ -472,6 +476,8 @@ public: void set_line_as_marked(int p_line, bool p_marked); void set_line_as_breakpoint(int p_line, bool p_breakpoint); bool is_line_set_as_breakpoint(int p_line) const; + void set_line_as_safe(int p_line, bool p_safe); + bool is_line_set_as_safe(int p_line) const; void get_breakpoints(List<int> *p_breakpoints) const; Array get_breakpoints_array() const; void remove_breakpoints(); diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 82d983184b..6e4fe88dbf 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -309,15 +309,23 @@ void TextureProgress::_notification(int p_what) { draw_texture_rect_region(progress, region, region, tint_progress); } break; case FILL_CLOCKWISE: - case FILL_COUNTER_CLOCKWISE: { + case FILL_COUNTER_CLOCKWISE: + case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { Rect2 region = Rect2(Point2(), s); draw_texture_rect_region(progress, region, region, tint_progress); } else if (val != 0) { Array pts; - float direction = mode == FILL_CLOCKWISE ? 1 : -1; - float start = rad_init_angle / 360; + float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; + float start; + + if (mode == FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE) { + start = rad_init_angle / 360 - val / 2; + } else { + start = rad_init_angle / 360; + } + float end = start + direction * val; pts.append(start); pts.append(end); @@ -351,6 +359,14 @@ void TextureProgress::_notification(int p_what) { draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2); } } break; + case FILL_BILINEAR_LEFT_AND_RIGHT: { + Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; + case FILL_BILINEAR_TOP_AND_BOTTOM: { + Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, region, tint_progress); + } break; default: draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } @@ -364,7 +380,7 @@ void TextureProgress::_notification(int p_what) { } void TextureProgress::set_fill_mode(int p_fill) { - ERR_FAIL_INDEX(p_fill, 6); + ERR_FAIL_INDEX(p_fill, 9); mode = (FillMode)p_fill; update(); } @@ -446,7 +462,7 @@ void TextureProgress::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture"); - ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode"); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom), Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over"); @@ -468,6 +484,9 @@ void TextureProgress::_bind_methods() { BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP); BIND_ENUM_CONSTANT(FILL_CLOCKWISE); BIND_ENUM_CONSTANT(FILL_COUNTER_CLOCKWISE); + BIND_ENUM_CONSTANT(FILL_BILINEAR_LEFT_AND_RIGHT); + BIND_ENUM_CONSTANT(FILL_BILINEAR_TOP_AND_BOTTOM); + BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE); } TextureProgress::TextureProgress() { diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index 34158b5db5..a11e55234a 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -52,7 +52,10 @@ public: FILL_TOP_TO_BOTTOM, FILL_BOTTOM_TO_TOP, FILL_CLOCKWISE, - FILL_COUNTER_CLOCKWISE + FILL_COUNTER_CLOCKWISE, + FILL_BILINEAR_LEFT_AND_RIGHT, + FILL_BILINEAR_TOP_AND_BOTTOM, + FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE }; void set_fill_mode(int p_fill); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 1d27612766..6f09488b64 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -121,7 +121,7 @@ void TreeItem::_cell_deselected(int p_cell) { void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); - Cell &c = cells[p_column]; + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; c.max = 100; @@ -144,7 +144,7 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].checked = p_checked; + cells.write[p_column].checked = p_checked; _changed_notify(p_column); } @@ -157,22 +157,22 @@ bool TreeItem::is_checked(int p_column) const { void TreeItem::set_text(int p_column, String p_text) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].text = p_text; + cells.write[p_column].text = p_text; if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE || cells[p_column].mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) { Vector<String> strings = p_text.split(","); - cells[p_column].min = INT_MAX; - cells[p_column].max = INT_MIN; + cells.write[p_column].min = INT_MAX; + cells.write[p_column].max = INT_MIN; for (int i = 0; i < strings.size(); i++) { int value = i; if (!strings[i].get_slicec(':', 1).empty()) { value = strings[i].get_slicec(':', 1).to_int(); } - cells[p_column].min = MIN(cells[p_column].min, value); - cells[p_column].max = MAX(cells[p_column].max, value); + cells.write[p_column].min = MIN(cells[p_column].min, value); + cells.write[p_column].max = MAX(cells[p_column].max, value); } - cells[p_column].step = 0; + cells.write[p_column].step = 0; } _changed_notify(p_column); } @@ -186,7 +186,7 @@ String TreeItem::get_text(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].suffix = p_suffix; + cells.write[p_column].suffix = p_suffix; _changed_notify(p_column); } @@ -200,7 +200,7 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon = p_icon; + cells.write[p_column].icon = p_icon; _changed_notify(p_column); } @@ -213,7 +213,7 @@ Ref<Texture> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_region = p_icon_region; + cells.write[p_column].icon_region = p_icon_region; _changed_notify(p_column); } @@ -226,7 +226,7 @@ Rect2 TreeItem::get_icon_region(int p_column) const { void TreeItem::set_icon_color(int p_column, const Color &p_icon_color) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_color = p_icon_color; + cells.write[p_column].icon_color = p_icon_color; _changed_notify(p_column); } @@ -239,7 +239,7 @@ Color TreeItem::get_icon_color(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].icon_max_w = p_max; + cells.write[p_column].icon_max_w = p_max; _changed_notify(p_column); } @@ -260,7 +260,7 @@ void TreeItem::set_range(int p_column, double p_value) { if (p_value > cells[p_column].max) p_value = cells[p_column].max; - cells[p_column].val = p_value; + cells.write[p_column].val = p_value; _changed_notify(p_column); } @@ -278,10 +278,10 @@ bool TreeItem::is_range_exponential(int p_column) const { void TreeItem::set_range_config(int p_column, double p_min, double p_max, double p_step, bool p_exp) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].min = p_min; - cells[p_column].max = p_max; - cells[p_column].step = p_step; - cells[p_column].expr = p_exp; + cells.write[p_column].min = p_min; + cells.write[p_column].max = p_max; + cells.write[p_column].step = p_step; + cells.write[p_column].expr = p_exp; _changed_notify(p_column); } @@ -296,7 +296,7 @@ void TreeItem::get_range_config(int p_column, double &r_min, double &r_max, doub void TreeItem::set_metadata(int p_column, const Variant &p_meta) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].meta = p_meta; + cells.write[p_column].meta = p_meta; } Variant TreeItem::get_metadata(int p_column) const { @@ -311,8 +311,8 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_NULL(p_object); - cells[p_column].custom_draw_obj = p_object->get_instance_id(); - cells[p_column].custom_draw_callback = p_callback; + cells.write[p_column].custom_draw_obj = p_object->get_instance_id(); + cells.write[p_column].custom_draw_callback = p_callback; } void TreeItem::set_collapsed(bool p_collapsed) { @@ -467,7 +467,7 @@ void TreeItem::remove_child(TreeItem *p_item) { void TreeItem::set_selectable(int p_column, bool p_selectable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].selectable = p_selectable; + cells.write[p_column].selectable = p_selectable; } bool TreeItem::is_selectable(int p_column) const { @@ -517,7 +517,7 @@ void TreeItem::add_button(int p_column, const Ref<Texture> &p_button, int p_id, button.id = p_id; button.disabled = p_disabled; button.tooltip = p_tooltip; - cells[p_column].buttons.push_back(button); + cells.write[p_column].buttons.push_back(button); _changed_notify(p_column); } @@ -540,7 +540,7 @@ void TreeItem::erase_button(int p_column, int p_idx) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons.remove(p_idx); + cells.write[p_column].buttons.remove(p_idx); _changed_notify(p_column); } @@ -568,7 +568,7 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture> &p_button) ERR_FAIL_COND(p_button.is_null()); ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons[p_idx].texture = p_button; + cells.write[p_column].buttons.write[p_idx].texture = p_button; _changed_notify(p_column); } @@ -576,14 +576,14 @@ void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells[p_column].buttons[p_idx].color = p_color; + cells.write[p_column].buttons.write[p_idx].color = p_color; _changed_notify(p_column); } void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].editable = p_editable; + cells.write[p_column].editable = p_editable; _changed_notify(p_column); } @@ -596,8 +596,8 @@ bool TreeItem::is_editable(int p_column) { void TreeItem::set_custom_color(int p_column, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_color = true; - cells[p_column].color = p_color; + cells.write[p_column].custom_color = true; + cells.write[p_column].color = p_color; _changed_notify(p_column); } Color TreeItem::get_custom_color(int p_column) const { @@ -610,15 +610,15 @@ Color TreeItem::get_custom_color(int p_column) const { void TreeItem::clear_custom_color(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_color = false; - cells[p_column].color = Color(); + cells.write[p_column].custom_color = false; + cells.write[p_column].color = Color(); _changed_notify(p_column); } void TreeItem::set_tooltip(int p_column, const String &p_tooltip) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].tooltip = p_tooltip; + cells.write[p_column].tooltip = p_tooltip; } String TreeItem::get_tooltip(int p_column) const { @@ -630,17 +630,17 @@ String TreeItem::get_tooltip(int p_column) const { void TreeItem::set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_bg_color = true; - cells[p_column].custom_bg_outline = p_bg_outline; - cells[p_column].bg_color = p_color; + cells.write[p_column].custom_bg_color = true; + cells.write[p_column].custom_bg_outline = p_bg_outline; + cells.write[p_column].bg_color = p_color; _changed_notify(p_column); } void TreeItem::clear_custom_bg_color(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_bg_color = false; - cells[p_column].bg_color = Color(); + cells.write[p_column].custom_bg_color = false; + cells.write[p_column].bg_color = Color(); _changed_notify(p_column); } @@ -655,7 +655,7 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].custom_button = p_button; + cells.write[p_column].custom_button = p_button; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -666,7 +666,7 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_align(int p_column, TextAlign p_align) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].text_align = p_align; + cells.write[p_column].text_align = p_align; _changed_notify(p_column); } @@ -678,7 +678,7 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); - cells[p_column].expand_right = p_enable; + cells.write[p_column].expand_right = p_enable; _changed_notify(p_column); } @@ -1486,7 +1486,7 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev, bool *r_in_range, bool p_force_deselect) { - TreeItem::Cell &selected_cell = p_selected->cells[p_col]; + TreeItem::Cell &selected_cell = p_selected->cells.write[p_col]; bool switched = false; if (r_in_range && !*r_in_range && (p_current == p_selected || p_current == p_prev)) { @@ -1498,7 +1498,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c for (int i = 0; i < columns.size(); i++) { - TreeItem::Cell &c = p_current->cells[i]; + TreeItem::Cell &c = p_current->cells.write[i]; if (!c.selectable) continue; @@ -1689,7 +1689,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool return -1; //collapse/uncollapse because nothing can be done with item } - TreeItem::Cell &c = p_item->cells[col]; + const TreeItem::Cell &c = p_item->cells[col]; bool already_selected = c.selected; bool already_cursor = (p_item == selected_item) && col == selected_col; @@ -1990,7 +1990,7 @@ void Tree::text_editor_enter(String p_text) { if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) return; - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; switch (c.mode) { case TreeItem::CELL_MODE_STRING: { @@ -2041,7 +2041,7 @@ void Tree::value_editor_changed(double p_value) { return; } - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; c.val = p_value; item_edited(popup_edited_item_col, popup_edited_item); update(); @@ -2055,7 +2055,7 @@ void Tree::popup_select(int p_option) { if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) return; - popup_edited_item->cells[popup_edited_item_col].val = p_option; + popup_edited_item->cells.write[popup_edited_item_col].val = p_option; //popup_edited_item->edited_signal.call( popup_edited_item_col ); update(); item_edited(popup_edited_item_col, popup_edited_item); @@ -2426,14 +2426,23 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { int col, h, section; TreeItem *it = _find_item_at_pos(root, mpos, col, h, section); - if ((drop_mode_flags && it != drop_mode_over) || section != drop_mode_section) { - drop_mode_over = it; - drop_mode_section = section; - update(); + if (drop_mode_flags) { + if (it != drop_mode_over) { + drop_mode_over = it; + update(); + } + if (it && section != drop_mode_section) { + drop_mode_section = section; + update(); + } } - if (it != cache.hover_item || col != cache.hover_cell) { + if (it != cache.hover_item) { cache.hover_item = it; + update(); + } + + if (it && col != cache.hover_cell) { cache.hover_cell = col; update(); } @@ -2458,7 +2467,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } } else { - TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; + const TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col]; float diff_y = -mm->get_relative().y; diff_y = Math::pow(ABS(diff_y), 1.8f) * SGN(diff_y); diff_y *= 0.1; @@ -2672,7 +2681,7 @@ bool Tree::edit_selected() { popup_edited_item = s; popup_edited_item_col = col; - TreeItem::Cell &c = s->cells[col]; + const TreeItem::Cell &c = s->cells[col]; if (c.mode == TreeItem::CELL_MODE_CHECK) { @@ -3063,7 +3072,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { if (!p_item->cells[p_column].selectable) return; - p_item->cells[p_column].selected = true; + p_item->cells.write[p_column].selected = true; //emit_signal("multi_selected",p_item,p_column,true); - NO this is for TreeItem::select selected_col = p_column; @@ -3077,7 +3086,7 @@ void Tree::item_selected(int p_column, TreeItem *p_item) { void Tree::item_deselected(int p_column, TreeItem *p_item) { if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) { - p_item->cells[p_column].selected = false; + p_item->cells.write[p_column].selected = false; } update(); } @@ -3158,14 +3167,14 @@ void Tree::set_column_min_width(int p_column, int p_min_width) { if (p_min_width < 1) return; - columns[p_column].min_width = p_min_width; + columns.write[p_column].min_width = p_min_width; update(); } void Tree::set_column_expand(int p_column, bool p_expand) { ERR_FAIL_INDEX(p_column, columns.size()); - columns[p_column].expand = p_expand; + columns.write[p_column].expand = p_expand; update(); } @@ -3413,7 +3422,7 @@ bool Tree::are_column_titles_visible() const { void Tree::set_column_title(int p_column, const String &p_title) { ERR_FAIL_INDEX(p_column, columns.size()); - columns[p_column].title = p_title; + columns.write[p_column].title = p_title; update(); } @@ -3659,7 +3668,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const { if (it) { - TreeItem::Cell &c = it->cells[col]; + const TreeItem::Cell &c = it->cells[col]; int col_width = get_column_width(col); for (int j = c.buttons.size() - 1; j >= 0; j--) { Ref<Texture> b = c.buttons[j].texture; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6d18cce21d..6144240328 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -725,6 +725,17 @@ const Map<StringName, MultiplayerAPI::RPCMode>::Element *Node::get_node_rset_mod return data.rpc_properties.find(p_property); } +bool Node::can_process_notification(int p_what) const { + switch (p_what) { + case NOTIFICATION_PHYSICS_PROCESS: return data.physics_process; + case NOTIFICATION_PROCESS: return data.idle_process; + case NOTIFICATION_INTERNAL_PROCESS: return data.idle_process_internal; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: return data.physics_process_internal; + } + + return true; +} + bool Node::can_process() const { ERR_FAIL_COND_V(!is_inside_tree(), false); @@ -809,6 +820,22 @@ bool Node::is_processing_internal() const { return data.idle_process_internal; } +void Node::set_process_priority(int p_priority) { + data.process_priority = p_priority; + + if (is_processing()) + data.tree->make_group_changed("idle_process"); + + if (is_processing_internal()) + data.tree->make_group_changed("idle_process_internal"); + + if (is_physics_processing()) + data.tree->make_group_changed("physics_process"); + + if (is_physics_processing_internal()) + data.tree->make_group_changed("physics_process_internal"); +} + void Node::set_process_input(bool p_enable) { if (p_enable == data.input) @@ -2608,6 +2635,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("is_physics_processing"), &Node::is_physics_processing); ClassDB::bind_method(D_METHOD("get_process_delta_time"), &Node::get_process_delta_time); ClassDB::bind_method(D_METHOD("set_process", "enable"), &Node::set_process); + ClassDB::bind_method(D_METHOD("set_process_priority", "priority"), &Node::set_process_priority); ClassDB::bind_method(D_METHOD("is_processing"), &Node::is_processing); ClassDB::bind_method(D_METHOD("set_process_input", "enable"), &Node::set_process_input); ClassDB::bind_method(D_METHOD("is_processing_input"), &Node::is_processing_input); @@ -2759,6 +2787,7 @@ Node::Node() { data.tree = NULL; data.physics_process = false; data.idle_process = false; + data.process_priority = 0; data.physics_process_internal = false; data.idle_process_internal = false; data.inside_tree = false; diff --git a/scene/main/node.h b/scene/main/node.h index 341349de79..f3422618ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -70,6 +70,11 @@ public: bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); } }; + struct ComparatorWithPriority { + + bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; } + }; + private: struct GroupData { @@ -118,6 +123,7 @@ private: //should move all the stuff below to bits bool physics_process; bool idle_process; + int process_priority; bool physics_process_internal; bool idle_process_internal; @@ -319,6 +325,8 @@ public: void set_process_internal(bool p_idle_process_internal); bool is_processing_internal() const; + void set_process_priority(int p_priority); + void set_process_input(bool p_enable); bool is_processing_input() const; @@ -355,6 +363,7 @@ public: void set_pause_mode(PauseMode p_mode); PauseMode get_pause_mode() const; bool can_process() const; + bool can_process_notification(int p_what) const; void request_ready(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 6438616cf2..e99f785848 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -132,6 +132,12 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { group_map.erase(E); } +void SceneTree::make_group_changed(const StringName &p_group) { + Map<StringName, Group>::Element *E = group_map.find(p_group); + if (E) + E->get().changed = true; +} + void SceneTree::flush_transform_notifications() { SelfList<Node> *n = xform_change_list.first(); @@ -165,18 +171,23 @@ void SceneTree::_flush_ugc() { ugc_locked = false; } -void SceneTree::_update_group_order(Group &g) { +void SceneTree::_update_group_order(Group &g, bool p_use_priority) { if (!g.changed) return; if (g.nodes.empty()) return; - Node **nodes = &g.nodes[0]; + Node **nodes = g.nodes.ptrw(); int node_count = g.nodes.size(); - SortArray<Node *, Node::Comparator> node_sort; - node_sort.sort(nodes, node_count); + if (p_use_priority) { + SortArray<Node *, Node::ComparatorWithPriority> node_sort; + node_sort.sort(nodes, node_count); + } else { + SortArray<Node *, Node::Comparator> node_sort; + node_sort.sort(nodes, node_count); + } g.changed = false; } @@ -216,7 +227,7 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -271,7 +282,7 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -320,7 +331,7 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group _update_group_order(g); Vector<Node *> nodes_copy = g.nodes; - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); call_lock++; @@ -884,7 +895,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p Vector<Node *> nodes_copy = g.nodes; int node_count = nodes_copy.size(); - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); Variant arg = p_input; const Variant *v[1] = { &arg }; @@ -921,14 +932,14 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (g.nodes.empty()) return; - _update_group_order(g); + _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); //copy, so copy on write happens in case something is removed from process while being called //performance is not lost because only if something is added/removed the vector is copied. Vector<Node *> nodes_copy = g.nodes; int node_count = nodes_copy.size(); - Node **nodes = &nodes_copy[0]; + Node **nodes = nodes_copy.ptrw(); call_lock++; @@ -940,6 +951,8 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio if (!n->can_process()) continue; + if (!n->can_process_notification(p_notification)) + continue; n->notification(p_notification); //ERR_FAIL_COND(node_count != g.nodes.size()); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index aa8d78b1e1..11201097d4 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -161,7 +161,7 @@ private: bool ugc_locked; void _flush_ugc(); - _FORCE_INLINE_ void _update_group_order(Group &g); + _FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false); void _update_listener(); Array _get_nodes_in_group(const StringName &p_group); @@ -204,6 +204,7 @@ private: Group *add_to_group(const StringName &p_group, Node *p_node); void remove_from_group(const StringName &p_group, Node *p_node); + void make_group_changed(const StringName &p_group); void _notify_group_pause(const StringName &p_group, int p_notification); void _call_input_pause(const StringName &p_group, const StringName &p_method, const Ref<InputEvent> &p_input); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 01302d4214..e43c2da02d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -41,7 +41,10 @@ #include "scene/3d/spatial.h" #include "scene/gui/control.h" #include "scene/gui/label.h" +#include "scene/gui/menu_button.h" #include "scene/gui/panel.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/popup_menu.h" #include "scene/main/timer.h" #include "scene/resources/mesh.h" #include "scene/scene_string_names.h" @@ -166,9 +169,9 @@ ViewportTexture::~ViewportTexture() { ///////////////////////////////////// -class TooltipPanel : public Panel { +class TooltipPanel : public PanelContainer { - GDCLASS(TooltipPanel, Panel) + GDCLASS(TooltipPanel, PanelContainer) public: TooltipPanel(){}; }; @@ -1305,10 +1308,11 @@ void Viewport::_gui_cancel_tooltip() { if (gui.tooltip_popup) { gui.tooltip_popup->queue_delete(); gui.tooltip_popup = NULL; + gui.tooltip_label = NULL; } } -String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) { +String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which) { Vector2 pos = p_pos; String tooltip; @@ -1317,6 +1321,10 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos) { tooltip = p_control->get_tooltip(pos); + if (r_which) { + *r_which = p_control; + } + if (tooltip != String()) break; pos = p_control->get_transform().xform(pos); @@ -1338,41 +1346,49 @@ void Viewport::_gui_show_tooltip() { return; } - String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos)); + Control *which = NULL; + String tooltip = _gui_get_tooltip(gui.tooltip, gui.tooltip->get_global_transform().xform_inv(gui.tooltip_pos), &which); if (tooltip.length() == 0) return; // bye if (gui.tooltip_popup) { memdelete(gui.tooltip_popup); gui.tooltip_popup = NULL; + gui.tooltip_label = NULL; } - if (!gui.tooltip) { + if (!which) { return; } - Control *rp = gui.tooltip->get_root_parent_control(); + Control *rp = which; //->get_root_parent_control(); if (!rp) return; - gui.tooltip_popup = memnew(TooltipPanel); + gui.tooltip_popup = which->make_custom_tooltip(tooltip); + + if (!gui.tooltip_popup) { + gui.tooltip_popup = memnew(TooltipPanel); + + gui.tooltip_label = memnew(TooltipLabel); + gui.tooltip_popup->add_child(gui.tooltip_label); + + Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel"); + + gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT)); + gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP)); + gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT)); + gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM)); + gui.tooltip_label->set_text(tooltip.strip_edges()); + } rp->add_child(gui.tooltip_popup); gui.tooltip_popup->force_parent_owned(); - gui.tooltip_label = memnew(TooltipLabel); - gui.tooltip_popup->add_child(gui.tooltip_label); gui.tooltip_popup->set_as_toplevel(true); - gui.tooltip_popup->hide(); + //gui.tooltip_popup->hide(); - Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel"); - - gui.tooltip_label->set_anchor_and_margin(MARGIN_LEFT, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_LEFT)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_TOP, Control::ANCHOR_BEGIN, ttp->get_margin(MARGIN_TOP)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT)); - gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM)); - gui.tooltip_label->set_text(tooltip.strip_edges()); - Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size()); - Rect2 vr = gui.tooltip_label->get_viewport_rect(); + Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_popup->get_minimum_size()); + Rect2 vr = gui.tooltip_popup->get_viewport_rect(); if (r.size.x + r.position.x > vr.size.x) r.position.x = vr.size.x - r.size.x; else if (r.position.x < 0) @@ -1840,8 +1856,32 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() == Variant::NIL && over && !gui.modal_stack.empty()) { Control *top = gui.modal_stack.back()->get(); + if (over != top && !top->is_a_parent_of(over)) { - over = NULL; //nothing can be found outside the modal stack + + PopupMenu *popup_menu = Object::cast_to<PopupMenu>(top); + MenuButton *popup_menu_parent = NULL; + MenuButton *menu_button = Object::cast_to<MenuButton>(over); + + if (popup_menu) + popup_menu_parent = Object::cast_to<MenuButton>(popup_menu->get_parent()); + + // If the mouse is over a menu button, this menu will open automatically + // if there is already a pop-up menu open at the same hierarchical level. + if (popup_menu_parent && menu_button && + popup_menu_parent->get_icon().is_null() && + menu_button->get_icon().is_null() && + (popup_menu->get_parent()->get_parent()->is_a_parent_of(menu_button) || + menu_button->get_parent()->is_a_parent_of(popup_menu))) { + + popup_menu->notification(Control::NOTIFICATION_MODAL_CLOSE); + popup_menu->_modal_stack_remove(); + popup_menu->hide(); + + menu_button->pressed(); + } else { + over = NULL; //nothing can be found outside the modal stack + } } } @@ -1891,13 +1931,18 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { bool is_tooltip_shown = false; if (gui.tooltip_popup) { - if (can_tooltip) { + if (can_tooltip && gui.tooltip) { String tooltip = _gui_get_tooltip(over, gui.tooltip->get_global_transform().xform_inv(mpos)); if (tooltip.length() == 0) _gui_cancel_tooltip(); - else if (tooltip == gui.tooltip_label->get_text()) + else if (gui.tooltip_label) { + if (tooltip == gui.tooltip_label->get_text()) { + is_tooltip_shown = true; + } + } else if (tooltip == String(gui.tooltip_popup->call("get_tooltip_text"))) { is_tooltip_shown = true; + } } else _gui_cancel_tooltip(); } diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 3000398540..e717d27069 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -255,7 +255,7 @@ private: Control *key_focus; Control *mouse_over; Control *tooltip; - Panel *tooltip_popup; + Control *tooltip_popup; Label *tooltip_label; Point2 tooltip_pos; Point2 last_mouse_pos; @@ -312,7 +312,7 @@ private: void _gui_remove_root_control(List<Control *>::Element *RI); void _gui_remove_subwindow_control(List<Control *>::Element *SI); - String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos); + String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_which = NULL); void _gui_cancel_tooltip(); void _gui_show_tooltip(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f42c1e0982..382bddfb4e 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -135,6 +135,7 @@ #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/cylinder_shape.h" #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/dynamic_font_stb.h" @@ -155,9 +156,12 @@ #include "scene/resources/sky_box.h" #include "scene/resources/sphere_shape.h" #include "scene/resources/surface_tool.h" +#include "scene/resources/text_file.h" #include "scene/resources/texture.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" +#include "scene/resources/visual_shader.h" +#include "scene/resources/visual_shader_nodes.h" #include "scene/resources/world.h" #include "scene/resources/world_2d.h" #include "scene/scene_string_names.h" @@ -196,10 +200,13 @@ #include "scene/3d/remote_transform.h" #include "scene/3d/room_instance.h" #include "scene/3d/skeleton.h" +#include "scene/3d/soft_body.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body.h" #include "scene/3d/visibility_notifier.h" +#include "scene/animation/skeleton_ik.h" #include "scene/resources/environment.h" +#include "scene/resources/physics_material.h" #endif static ResourceFormatLoaderTheme *resource_loader_theme = NULL; @@ -210,6 +217,7 @@ static ResourceFormatLoaderText *resource_loader_text = NULL; static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font = NULL; static ResourceFormatLoaderStreamTexture *resource_loader_stream_texture = NULL; +static ResourceFormatLoaderTextureLayered *resource_loader_texture_layered = NULL; static ResourceFormatLoaderBMFont *resource_loader_bmfont = NULL; @@ -230,6 +238,9 @@ void register_scene_types() { resource_loader_stream_texture = memnew(ResourceFormatLoaderStreamTexture); ResourceLoader::add_resource_format_loader(resource_loader_stream_texture); + resource_loader_texture_layered = memnew(ResourceFormatLoaderTextureLayered); + ResourceLoader::add_resource_format_loader(resource_loader_texture_layered); + resource_loader_theme = memnew(ResourceFormatLoaderTheme); ResourceLoader::add_resource_format_loader(resource_loader_theme); @@ -309,21 +320,19 @@ void register_scene_types() { ClassDB::register_class<CenterContainer>(); ClassDB::register_class<ScrollContainer>(); ClassDB::register_class<PanelContainer>(); - ClassDB::register_virtual_class<SplitContainer>(); - ClassDB::register_class<HSplitContainer>(); - ClassDB::register_class<VSplitContainer>(); - ClassDB::register_class<GraphNode>(); - ClassDB::register_class<GraphEdit>(); OS::get_singleton()->yield(); //may take time to init ClassDB::register_class<TextureProgress>(); ClassDB::register_class<ItemList>(); + ClassDB::register_class<LineEdit>(); + ClassDB::register_class<VideoPlayer>(); + #ifndef ADVANCED_GUI_DISABLED ClassDB::register_class<FileDialog>(); - ClassDB::register_class<LineEdit>(); + ClassDB::register_class<PopupMenu>(); ClassDB::register_class<Tree>(); @@ -340,9 +349,13 @@ void register_scene_types() { ClassDB::register_class<WindowDialog>(); ClassDB::register_class<AcceptDialog>(); ClassDB::register_class<ConfirmationDialog>(); - ClassDB::register_class<VideoPlayer>(); ClassDB::register_class<MarginContainer>(); ClassDB::register_class<ViewportContainer>(); + ClassDB::register_virtual_class<SplitContainer>(); + ClassDB::register_class<HSplitContainer>(); + ClassDB::register_class<VSplitContainer>(); + ClassDB::register_class<GraphNode>(); + ClassDB::register_class<GraphEdit>(); OS::get_singleton()->yield(); //may take time to init @@ -353,6 +366,7 @@ void register_scene_types() { ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); + ClassDB::register_class<SkeletonIK>(); ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); @@ -422,6 +436,7 @@ void register_scene_types() { ClassDB::register_class<KinematicCollision>(); ClassDB::register_class<KinematicBody>(); ClassDB::register_class<PhysicalBone>(); + ClassDB::register_class<SoftBody>(); ClassDB::register_class<VehicleBody>(); ClassDB::register_class<VehicleWheel>(); @@ -455,6 +470,39 @@ void register_scene_types() { AcceptDialog::set_swap_ok_cancel(GLOBAL_DEF("gui/common/swap_ok_cancel", bool(OS::get_singleton()->get_swap_ok_cancel()))); ClassDB::register_class<Shader>(); + ClassDB::register_class<VisualShader>(); + ClassDB::register_virtual_class<VisualShaderNode>(); + ClassDB::register_class<VisualShaderNodeInput>(); + ClassDB::register_virtual_class<VisualShaderNodeOutput>(); + ClassDB::register_class<VisualShaderNodeScalarConstant>(); + ClassDB::register_class<VisualShaderNodeColorConstant>(); + ClassDB::register_class<VisualShaderNodeVec3Constant>(); + ClassDB::register_class<VisualShaderNodeTransformConstant>(); + ClassDB::register_class<VisualShaderNodeScalarOp>(); + ClassDB::register_class<VisualShaderNodeVectorOp>(); + ClassDB::register_class<VisualShaderNodeColorOp>(); + ClassDB::register_class<VisualShaderNodeTransformMult>(); + ClassDB::register_class<VisualShaderNodeTransformVecMult>(); + ClassDB::register_class<VisualShaderNodeScalarFunc>(); + ClassDB::register_class<VisualShaderNodeVectorFunc>(); + ClassDB::register_class<VisualShaderNodeDotProduct>(); + ClassDB::register_class<VisualShaderNodeVectorLen>(); + ClassDB::register_class<VisualShaderNodeScalarInterp>(); + ClassDB::register_class<VisualShaderNodeVectorInterp>(); + ClassDB::register_class<VisualShaderNodeVectorCompose>(); + ClassDB::register_class<VisualShaderNodeTransformCompose>(); + ClassDB::register_class<VisualShaderNodeVectorDecompose>(); + ClassDB::register_class<VisualShaderNodeTransformDecompose>(); + ClassDB::register_class<VisualShaderNodeTexture>(); + ClassDB::register_class<VisualShaderNodeCubeMap>(); + ClassDB::register_virtual_class<VisualShaderNodeUniform>(); + ClassDB::register_class<VisualShaderNodeScalarUniform>(); + ClassDB::register_class<VisualShaderNodeColorUniform>(); + ClassDB::register_class<VisualShaderNodeVec3Uniform>(); + ClassDB::register_class<VisualShaderNodeTransformUniform>(); + ClassDB::register_class<VisualShaderNodeTextureUniform>(); + ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); + ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); ClassDB::register_class<CanvasItemMaterial>(); @@ -541,6 +589,7 @@ void register_scene_types() { ClassDB::register_class<SphereShape>(); ClassDB::register_class<BoxShape>(); ClassDB::register_class<CapsuleShape>(); + ClassDB::register_class<CylinderShape>(); ClassDB::register_class<PlaneShape>(); ClassDB::register_class<ConvexPolygonShape>(); ClassDB::register_class<ConcavePolygonShape>(); @@ -551,6 +600,8 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init ClassDB::register_class<SpatialVelocityTracker>(); + + ClassDB::register_class<PhysicsMaterial>(); #endif ClassDB::register_class<World>(); ClassDB::register_class<Environment>(); @@ -566,12 +617,18 @@ void register_scene_types() { ClassDB::register_class<CurveTexture>(); ClassDB::register_class<GradientTexture>(); ClassDB::register_class<ProxyTexture>(); + ClassDB::register_class<AnimatedTexture>(); ClassDB::register_class<CubeMap>(); + ClassDB::register_virtual_class<TextureLayered>(); + ClassDB::register_class<Texture3D>(); + ClassDB::register_class<TextureArray>(); ClassDB::register_class<Animation>(); ClassDB::register_virtual_class<Font>(); ClassDB::register_class<BitmapFont>(); ClassDB::register_class<Curve>(); + ClassDB::register_class<TextFile>(); + ClassDB::register_class<DynamicFontData>(); ClassDB::register_class<DynamicFont>(); @@ -678,6 +735,7 @@ void unregister_scene_types() { memdelete(resource_loader_dynamic_font); memdelete(resource_loader_stream_texture); + memdelete(resource_loader_texture_layered); memdelete(resource_loader_theme); DynamicFont::finish_dynamic_fonts(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index e57a2b68c8..7041b62487 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -100,7 +100,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < (vcount / 12); i++) { - TKey<TransformKey> &tk = tt->transforms[i]; + TKey<TransformKey> &tk = tt->transforms.write[i]; const float *ofs = &r[i * 12]; tk.time = ofs[0]; tk.transition = ofs[1]; @@ -154,8 +154,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - vt->values[i].time = rt[i]; - vt->values[i].value = values[i]; + vt->values.write[i].time = rt[i]; + vt->values.write[i].value = values[i]; } if (d.has("transitions")) { @@ -167,7 +167,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - vt->values[i].transition = rtr[i]; + vt->values.write[i].transition = rtr[i]; } } } @@ -235,13 +235,13 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { for (int i = 0; i < valcount; i++) { - bt->values[i].time = rt[i]; - bt->values[i].transition = 0; //unused in bezier - bt->values[i].value.value = rv[i * 5 + 0]; - bt->values[i].value.in_handle.x = rv[i * 5 + 1]; - bt->values[i].value.in_handle.y = rv[i * 5 + 2]; - bt->values[i].value.out_handle.x = rv[i * 5 + 3]; - bt->values[i].value.out_handle.y = rv[i * 5 + 4]; + bt->values.write[i].time = rt[i]; + bt->values.write[i].transition = 0; //unused in bezier + bt->values.write[i].value.value = rv[i * 5 + 0]; + bt->values.write[i].value.in_handle.x = rv[i * 5 + 1]; + bt->values.write[i].value.in_handle.y = rv[i * 5 + 2]; + bt->values.write[i].value.out_handle.x = rv[i * 5 + 3]; + bt->values.write[i].value.out_handle.y = rv[i * 5 + 4]; } } @@ -313,7 +313,7 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) { TKey<StringName> ak; ak.time = rt[i]; ak.value = rc[i]; - an->values[i] = ak; + an->values.write[i] = ak; } } @@ -822,7 +822,7 @@ int Animation::_insert(float p_time, T &p_keys, const V &p_value) { } else if (p_keys[idx - 1].time == p_time) { // condition for replacing. - p_keys[idx - 1] = p_value; + p_keys.write[idx - 1] = p_value; return idx - 1; } @@ -1349,18 +1349,18 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); Dictionary d = p_value; if (d.has("location")) - tt->transforms[p_key_idx].value.loc = d["location"]; + tt->transforms.write[p_key_idx].value.loc = d["location"]; if (d.has("rotation")) - tt->transforms[p_key_idx].value.rot = d["rotation"]; + tt->transforms.write[p_key_idx].value.rot = d["rotation"]; if (d.has("scale")) - tt->transforms[p_key_idx].value.scale = d["scale"]; + tt->transforms.write[p_key_idx].value.scale = d["scale"]; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); - vt->values[p_key_idx].value = p_value; + vt->values.write[p_key_idx].value = p_value; } break; case TYPE_METHOD: { @@ -1369,9 +1369,9 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_INDEX(p_key_idx, mt->methods.size()); Dictionary d = p_value; if (d.has("method")) - mt->methods[p_key_idx].method = d["method"]; + mt->methods.write[p_key_idx].method = d["method"]; if (d.has("args")) - mt->methods[p_key_idx].params = d["args"]; + mt->methods.write[p_key_idx].params = d["args"]; } break; case TYPE_BEZIER: { @@ -1381,11 +1381,11 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p Array arr = p_value; ERR_FAIL_COND(arr.size() != 5); - bt->values[p_key_idx].value.value = arr[0]; - bt->values[p_key_idx].value.in_handle.x = arr[1]; - bt->values[p_key_idx].value.in_handle.y = arr[2]; - bt->values[p_key_idx].value.out_handle.x = arr[3]; - bt->values[p_key_idx].value.out_handle.y = arr[4]; + bt->values.write[p_key_idx].value.value = arr[0]; + bt->values.write[p_key_idx].value.in_handle.x = arr[1]; + bt->values.write[p_key_idx].value.in_handle.y = arr[2]; + bt->values.write[p_key_idx].value.out_handle.x = arr[3]; + bt->values.write[p_key_idx].value.out_handle.y = arr[4]; } break; case TYPE_AUDIO: { @@ -1397,16 +1397,16 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p ERR_FAIL_COND(!k.has("end_offset")); ERR_FAIL_COND(!k.has("stream")); - at->values[p_key_idx].value.start_offset = k["start_offset"]; - at->values[p_key_idx].value.end_offset = k["end_offset"]; - at->values[p_key_idx].value.stream = k["stream"]; + at->values.write[p_key_idx].value.start_offset = k["start_offset"]; + at->values.write[p_key_idx].value.end_offset = k["end_offset"]; + at->values.write[p_key_idx].value.stream = k["stream"]; } break; case TYPE_ANIMATION: { AnimationTrack *at = static_cast<AnimationTrack *>(t); - at->values[p_key_idx].value = p_value; + at->values.write[p_key_idx].value = p_value; } break; } @@ -1423,20 +1423,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, float p_tra TransformTrack *tt = static_cast<TransformTrack *>(t); ERR_FAIL_INDEX(p_key_idx, tt->transforms.size()); - tt->transforms[p_key_idx].transition = p_transition; + tt->transforms.write[p_key_idx].transition = p_transition; } break; case TYPE_VALUE: { ValueTrack *vt = static_cast<ValueTrack *>(t); ERR_FAIL_INDEX(p_key_idx, vt->values.size()); - vt->values[p_key_idx].transition = p_transition; + vt->values.write[p_key_idx].transition = p_transition; } break; case TYPE_METHOD: { MethodTrack *mt = static_cast<MethodTrack *>(t); ERR_FAIL_INDEX(p_key_idx, mt->methods.size()); - mt->methods[p_key_idx].transition = p_transition; + mt->methods.write[p_key_idx].transition = p_transition; } break; case TYPE_BEZIER: @@ -2210,7 +2210,7 @@ void Animation::bezier_track_set_key_value(int p_track, int p_index, float p_val ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.value = p_value; + bt->values.write[p_index].value.value = p_value; emit_changed(); } @@ -2224,9 +2224,9 @@ void Animation::bezier_track_set_key_in_handle(int p_track, int p_index, const V ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.in_handle = p_handle; + bt->values.write[p_index].value.in_handle = p_handle; if (bt->values[p_index].value.in_handle.x > 0) { - bt->values[p_index].value.in_handle.x = 0; + bt->values.write[p_index].value.in_handle.x = 0; } emit_changed(); } @@ -2240,9 +2240,9 @@ void Animation::bezier_track_set_key_out_handle(int p_track, int p_index, const ERR_FAIL_INDEX(p_index, bt->values.size()); - bt->values[p_index].value.out_handle = p_handle; + bt->values.write[p_index].value.out_handle = p_handle; if (bt->values[p_index].value.out_handle.x < 0) { - bt->values[p_index].value.out_handle.x = 0; + bt->values.write[p_index].value.out_handle.x = 0; } emit_changed(); } @@ -2329,13 +2329,14 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const { int iterations = 10; - float low = 0; - float high = bt->values[idx + 1].time - bt->values[idx].time; + float duration = bt->values[idx + 1].time - bt->values[idx].time; // time duration between our two keyframes + float low = 0; // 0% of the current animation segment + float high = 1; // 100% of the current animation segment float middle = 0; Vector2 start(0, bt->values[idx].value.value); Vector2 start_out = start + bt->values[idx].value.out_handle; - Vector2 end(high, bt->values[idx + 1].value.value); + Vector2 end(duration, bt->values[idx + 1].value.value); Vector2 end_in = end + bt->values[idx + 1].value.in_handle; //narrow high and low as much as possible @@ -2355,7 +2356,6 @@ float Animation::bezier_track_interpolate(int p_track, float p_time) const { //interpolate the result: Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end); Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end); - float c = (t - low_pos.x) / (high_pos.x - low_pos.x); return low_pos.linear_interpolate(high_pos, c).y; @@ -2396,7 +2396,7 @@ void Animation::audio_track_set_key_stream(int p_track, int p_key, const RES &p_ ERR_FAIL_INDEX(p_key, at->values.size()); - at->values[p_key].value.stream = p_stream; + at->values.write[p_key].value.stream = p_stream; emit_changed(); } @@ -2414,7 +2414,7 @@ void Animation::audio_track_set_key_start_offset(int p_track, int p_key, float p if (p_offset < 0) p_offset = 0; - at->values[p_key].value.start_offset = p_offset; + at->values.write[p_key].value.start_offset = p_offset; emit_changed(); } @@ -2432,7 +2432,7 @@ void Animation::audio_track_set_key_end_offset(int p_track, int p_key, float p_o if (p_offset < 0) p_offset = 0; - at->values[p_key].value.end_offset = p_offset; + at->values.write[p_key].value.end_offset = p_offset; emit_changed(); } @@ -2505,7 +2505,7 @@ void Animation::animation_track_set_key_animation(int p_track, int p_key, const ERR_FAIL_INDEX(p_key, at->values.size()); - at->values[p_key].value = p_animation; + at->values.write[p_key].value = p_animation; emit_changed(); } @@ -2550,7 +2550,7 @@ void Animation::track_move_up(int p_track) { if (p_track >= 0 && p_track < (tracks.size() - 1)) { - SWAP(tracks[p_track], tracks[p_track + 1]); + SWAP(tracks.write[p_track], tracks.write[p_track + 1]); } emit_changed(); @@ -2585,7 +2585,7 @@ void Animation::track_move_down(int p_track) { if (p_track > 0 && p_track < tracks.size()) { - SWAP(tracks[p_track], tracks[p_track - 1]); + SWAP(tracks.write[p_track], tracks.write[p_track - 1]); } emit_changed(); } @@ -2596,7 +2596,7 @@ void Animation::track_swap(int p_track, int p_with_track) { ERR_FAIL_INDEX(p_with_track, tracks.size()); if (p_track == p_with_track) return; - SWAP(tracks[p_track], tracks[p_with_track]); + SWAP(tracks.write[p_track], tracks.write[p_with_track]); emit_changed(); } @@ -2927,9 +2927,9 @@ void Animation::_transform_track_optimize(int p_idx, float p_allowed_linear_err, for (int i = 1; i < tt->transforms.size() - 1; i++) { - TKey<TransformKey> &t0 = tt->transforms[i - 1]; - TKey<TransformKey> &t1 = tt->transforms[i]; - TKey<TransformKey> &t2 = tt->transforms[i + 1]; + TKey<TransformKey> &t0 = tt->transforms.write[i - 1]; + TKey<TransformKey> &t1 = tt->transforms.write[i]; + TKey<TransformKey> &t2 = tt->transforms.write[i + 1]; bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm); if (erase && !prev_erased) { diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp index 02a9e4d69b..e6a4b01deb 100644 --- a/scene/resources/audio_stream_sample.cpp +++ b/scene/resources/audio_stream_sample.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "audio_stream_sample.h" +#include "io/marshalls.h" +#include "os/file_access.h" void AudioStreamPlaybackSample::start(float p_from_pos) { @@ -509,6 +511,76 @@ PoolVector<uint8_t> AudioStreamSample::get_data() const { return pv; } +void AudioStreamSample::save_to_wav(String p_path) { + if (format == AudioStreamSample::FORMAT_IMA_ADPCM) { + WARN_PRINTS("Saving IMA_ADPC samples are not supported yet"); + return; + } + + int sub_chunk_2_size = data_bytes; //Subchunk2Size = Size of data in bytes + + // Format code + // 1:PCM format (for 8 or 16 bit) + // 3:IEEE float format + int format_code = (format == FORMAT_IMA_ADPCM) ? 3 : 1; + + int n_channels = stereo ? 2 : 1; + + long sample_rate = mix_rate; + + int byte_pr_sample = 0; + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: byte_pr_sample = 1; break; + case AudioStreamSample::FORMAT_16_BITS: byte_pr_sample = 2; break; + case AudioStreamSample::FORMAT_IMA_ADPCM: byte_pr_sample = 4; break; + } + + String file_path = p_path; + if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) { + file_path += ".wav"; + } + + Error err; + FileAccess *file = FileAccess::open(file_path, FileAccess::WRITE, &err); //Overrides existing file if present + + // Create WAV Header + file->store_string("RIFF"); //ChunkID + file->store_32(sub_chunk_2_size + 36); //ChunkSize = 36 + SubChunk2Size (size of entire file minus the 8 bits for this and previous header) + file->store_string("WAVE"); //Format + file->store_string("fmt "); //Subchunk1ID + file->store_32(16); //Subchunk1Size = 16 + file->store_16(format_code); //AudioFormat + file->store_16(n_channels); //Number of Channels + file->store_32(sample_rate); //SampleRate + file->store_32(sample_rate * n_channels * byte_pr_sample); //ByteRate + file->store_16(n_channels * byte_pr_sample); //BlockAlign = NumChannels * BytePrSample + file->store_16(byte_pr_sample * 8); //BitsPerSample + file->store_string("data"); //Subchunk2ID + file->store_32(sub_chunk_2_size); //Subchunk2Size + + // Add data + PoolVector<uint8_t>::Read read_data = get_data().read(); + switch (format) { + case AudioStreamSample::FORMAT_8_BITS: + for (int i = 0; i < data_bytes; i++) { + uint8_t data_point = (read_data[i] + 128); + file->store_8(data_point); + } + break; + case AudioStreamSample::FORMAT_16_BITS: + for (int i = 0; i < data_bytes / 2; i++) { + uint16_t data_point = decode_uint16(&read_data[i * 2]); + file->store_16(data_point); + } + break; + case AudioStreamSample::FORMAT_IMA_ADPCM: + //Unimplemented + break; + } + + file->close(); +} + Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() { Ref<AudioStreamPlaybackSample> sample; @@ -545,6 +617,8 @@ void AudioStreamSample::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo); ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo); + ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav); + ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data"); ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong"), "set_loop_mode", "get_loop_mode"); diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h index 5fe65c194e..a27acc92b7 100644 --- a/scene/resources/audio_stream_sample.h +++ b/scene/resources/audio_stream_sample.h @@ -140,6 +140,8 @@ public: void set_data(const PoolVector<uint8_t> &p_data); PoolVector<uint8_t> get_data() const; + void save_to_wav(String p_path); + virtual Ref<AudioStreamPlayback> instance_playback(); virtual String get_stream_name() const; diff --git a/scene/resources/bit_mask.cpp b/scene/resources/bit_mask.cpp index 29ffefd9d6..ec1aa7ec46 100644 --- a/scene/resources/bit_mask.cpp +++ b/scene/resources/bit_mask.cpp @@ -130,7 +130,7 @@ void BitMap::set_bit(const Point2 &p_pos, bool p_value) { else b &= ~(1 << bbit); - bitmask[bbyte] = b; + bitmask.write[bbyte] = b; } bool BitMap::get_bit(const Point2 &p_pos) const { @@ -322,8 +322,8 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points[_points.size() - 1].x = (float)(curx - rect.position.x); - _points[_points.size() - 1].y = (float)(cury + rect.position.y); + _points.write[_points.size() - 1].x = (float)(curx - rect.position.x); + _points.write[_points.size() - 1].y = (float)(cury + rect.position.y); } else { _points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y))); } @@ -373,11 +373,11 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { Vector<Vector2> left, right; left.resize(index); for (int i = 0; i < index; i++) { - left[i] = v[i]; + left.write[i] = v[i]; } right.resize(v.size() - index); for (int i = 0; i < right.size(); i++) { - right[i] = v[index + i]; + right.write[i] = v[index + i]; } Vector<Vector2> r1 = rdp(left, optimization); Vector<Vector2> r2 = rdp(right, optimization); @@ -385,7 +385,7 @@ static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) { int middle = r1.size(); r1.resize(r1.size() + r2.size()); for (int i = 0; i < r2.size(); i++) { - r1[middle + i] = r2[i]; + r1.write[middle + i] = r2[i]; } return r1; } else { @@ -412,37 +412,97 @@ static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, Vector2 last = result[result.size() - 1]; if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) { - result[0].y = last.y; + result.write[0].y = last.y; result.resize(result.size() - 1); } return result; } +struct FillBitsStackEntry { + Point2i pos; + int i; + int j; +}; + static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) { - for (int i = p_pos.x - 1; i <= p_pos.x + 1; i++) { - for (int j = p_pos.y - 1; j <= p_pos.y + 1; j++) { + // Using a custom stack to work iteratively to avoid stack overflow on big bitmaps + PoolVector<FillBitsStackEntry> stack; + // Tracking size since we won't be shrinking the stack vector + int stack_size = 0; - if (i < rect.position.x || i >= rect.position.x + rect.size.x) - continue; - if (j < rect.position.y || j >= rect.position.y + rect.size.y) - continue; + Point2i pos = p_pos; + int next_i; + int next_j; - if (p_map->get_bit(Vector2(i, j))) - continue; + bool reenter = true; + bool popped = false; + do { + if (reenter) { + next_i = pos.x - 1; + next_j = pos.y - 1; + reenter = false; + } + + for (int i = next_i; i <= pos.x + 1; i++) { + for (int j = next_j; j <= pos.y + 1; j++) { + if (popped) { + // The next loop over j must start normally + next_j = pos.y; + popped = false; + // Skip because an iteration was already executed with current counter values + continue; + } - else if (p_src->get_bit(Vector2(i, j))) { - p_map->set_bit(Vector2(i, j), true); - fill_bits(p_src, p_map, Point2i(i, j), rect); + if (i < rect.position.x || i >= rect.position.x + rect.size.x) + continue; + if (j < rect.position.y || j >= rect.position.y + rect.size.y) + continue; + + if (p_map->get_bit(Vector2(i, j))) + continue; + + else if (p_src->get_bit(Vector2(i, j))) { + p_map->set_bit(Vector2(i, j), true); + + FillBitsStackEntry se = { pos, i, j }; + stack.resize(MAX(stack_size + 1, stack.size())); + stack.set(stack_size, se); + stack_size++; + + pos = Point2i(i, j); + reenter = true; + break; + } + } + if (reenter) { + break; } } - } + if (!reenter) { + if (stack_size) { + FillBitsStackEntry se = stack.get(stack_size - 1); + stack_size--; + pos = se.pos; + next_i = se.i; + next_j = se.j; + popped = true; + } + } + } while (reenter || popped); + +#ifdef DEBUG_ENABLED + print_line("max stack size: " + itos(stack.size())); +#endif } + Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).clip(p_rect); +#ifdef DEBUG_ENABLED print_line("Rect: " + r); +#endif Point2i from; Ref<BitMap> fill; fill.instance(); @@ -454,9 +514,13 @@ Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, fl if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) { Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); +#ifdef DEBUG_ENABLED print_line("pre reduce: " + itos(polygon.size())); +#endif polygon = reduce(polygon, r, p_epsilon); +#ifdef DEBUG_ENABLED print_line("post reduce: " + itos(polygon.size())); +#endif polygons.push_back(polygon); fill_bits(this, fill, Point2i(j, i), r); } @@ -510,6 +574,34 @@ void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) { } } +Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const { + + Vector<Vector<Vector2> > result = clip_opaque_to_polygons(p_rect, p_epsilon); + + // Convert result to bindable types + + Array result_array; + result_array.resize(result.size()); + for (int i = 0; i < result.size(); i++) { + + const Vector<Vector2> &polygon = result[i]; + + PoolVector2Array polygon_array; + polygon_array.resize(polygon.size()); + + { + PoolVector2Array::Write w = polygon_array.write(); + for (int j = 0; j < polygon.size(); j++) { + w[j] = polygon[j]; + } + } + + result_array[i] = polygon_array; + } + + return result_array; +} + void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); @@ -526,6 +618,9 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); + ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); + ClassDB::bind_method(D_METHOD("opaque_to_polygons", "rect", "epsilon"), &BitMap::_opaque_to_polygons_bind, DEFVAL(2.0)); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); } diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h index dcd5edb4fb..40f0bfb04a 100644 --- a/scene/resources/bit_mask.h +++ b/scene/resources/bit_mask.h @@ -46,6 +46,8 @@ class BitMap : public Resource { Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const; + Array _opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) const; + protected: void _set_data(const Dictionary &p_d); Dictionary _get_data() const; diff --git a/scene/resources/color_ramp.cpp b/scene/resources/color_ramp.cpp index b2f586d02d..4a43303d84 100644 --- a/scene/resources/color_ramp.cpp +++ b/scene/resources/color_ramp.cpp @@ -40,10 +40,10 @@ Gradient::Gradient() { //Set initial color ramp transition from black to white points.resize(2); - points[0].color = Color(0, 0, 0, 1); - points[0].offset = 0; - points[1].color = Color(1, 1, 1, 1); - points[1].offset = 1; + points.write[0].color = Color(0, 0, 0, 1); + points.write[0].offset = 0; + points.write[1].color = Color(1, 1, 1, 1); + points.write[1].offset = 1; is_sorted = true; } @@ -79,7 +79,7 @@ Vector<float> Gradient::get_offsets() const { Vector<float> offsets; offsets.resize(points.size()); for (int i = 0; i < points.size(); i++) { - offsets[i] = points[i].offset; + offsets.write[i] = points[i].offset; } return offsets; } @@ -88,7 +88,7 @@ Vector<Color> Gradient::get_colors() const { Vector<Color> colors; colors.resize(points.size()); for (int i = 0; i < points.size(); i++) { - colors[i] = points[i].color; + colors.write[i] = points[i].color; } return colors; } @@ -96,7 +96,7 @@ Vector<Color> Gradient::get_colors() const { void Gradient::set_offsets(const Vector<float> &p_offsets) { points.resize(p_offsets.size()); for (int i = 0; i < points.size(); i++) { - points[i].offset = p_offsets[i]; + points.write[i].offset = p_offsets[i]; } is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); @@ -107,7 +107,7 @@ void Gradient::set_colors(const Vector<Color> &p_colors) { is_sorted = false; points.resize(p_colors.size()); for (int i = 0; i < points.size(); i++) { - points[i].color = p_colors[i]; + points.write[i].color = p_colors[i]; } emit_signal(CoreStringNames::get_singleton()->changed); } @@ -144,7 +144,7 @@ void Gradient::set_points(Vector<Gradient::Point> &p_points) { void Gradient::set_offset(int pos, const float offset) { if (points.size() <= pos) points.resize(pos + 1); - points[pos].offset = offset; + points.write[pos].offset = offset; is_sorted = false; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -160,7 +160,7 @@ void Gradient::set_color(int pos, const Color &color) { points.resize(pos + 1); is_sorted = false; } - points[pos].color = color; + points.write[pos].color = color; emit_signal(CoreStringNames::get_singleton()->changed); } diff --git a/scene/resources/color_ramp.h b/scene/resources/color_ramp.h index c042a0d3d0..070ad7f0d3 100644 --- a/scene/resources/color_ramp.h +++ b/scene/resources/color_ramp.h @@ -98,7 +98,7 @@ public: while (low <= high) { middle = (low + high) / 2; - Point &point = points[middle]; + const Point &point = points[middle]; if (point.offset > p_offset) { high = middle - 1; //search low end of array } else if (point.offset < p_offset) { @@ -118,8 +118,8 @@ public: return points[points.size() - 1].color; if (first < 0) return points[0].color; - Point &pointFirst = points[first]; - Point &pointSecond = points[second]; + const Point &pointFirst = points[first]; + const Point &pointSecond = points[second]; return pointFirst.color.linear_interpolate(pointSecond.color, (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset)); } diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp index 935f041837..bc9e2848b3 100644 --- a/scene/resources/concave_polygon_shape.cpp +++ b/scene/resources/concave_polygon_shape.cpp @@ -56,8 +56,8 @@ Vector<Vector3> ConcavePolygonShape::_gen_debug_mesh_lines() { int idx = 0; for (Set<DrawEdge>::Element *E = edges.front(); E; E = E->next()) { - points[idx + 0] = E->get().a; - points[idx + 1] = E->get().b; + points.write[idx + 0] = E->get().a; + points.write[idx + 1] = E->get().b; idx += 2; } diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp index a2e0996996..fa9369d3bc 100644 --- a/scene/resources/convex_polygon_shape.cpp +++ b/scene/resources/convex_polygon_shape.cpp @@ -46,8 +46,8 @@ Vector<Vector3> ConvexPolygonShape::_gen_debug_mesh_lines() { Vector<Vector3> lines; lines.resize(md.edges.size() * 2); for (int i = 0; i < md.edges.size(); i++) { - lines[i * 2 + 0] = md.vertices[md.edges[i].a]; - lines[i * 2 + 1] = md.vertices[md.edges[i].b]; + lines.write[i * 2 + 0] = md.vertices[md.edges[i].a]; + lines.write[i * 2 + 1] = md.vertices[md.edges[i].b]; } return lines; } diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index f2fd919f20..d8989bf062 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -153,25 +153,25 @@ void Curve::clean_dupes() { void Curve::set_point_left_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].left_tangent = tangent; - _points[i].left_mode = TANGENT_FREE; + _points.write[i].left_tangent = tangent; + _points.write[i].left_mode = TANGENT_FREE; mark_dirty(); } void Curve::set_point_right_tangent(int i, real_t tangent) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].right_tangent = tangent; - _points[i].right_mode = TANGENT_FREE; + _points.write[i].right_tangent = tangent; + _points.write[i].right_mode = TANGENT_FREE; mark_dirty(); } void Curve::set_point_left_mode(int i, TangentMode p_mode) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].left_mode = p_mode; + _points.write[i].left_mode = p_mode; if (i > 0) { if (p_mode == TANGENT_LINEAR) { Vector2 v = (_points[i - 1].pos - _points[i].pos).normalized(); - _points[i].left_tangent = v.y / v.x; + _points.write[i].left_tangent = v.y / v.x; } } mark_dirty(); @@ -179,11 +179,11 @@ void Curve::set_point_left_mode(int i, TangentMode p_mode) { void Curve::set_point_right_mode(int i, TangentMode p_mode) { ERR_FAIL_INDEX(i, _points.size()); - _points[i].right_mode = p_mode; + _points.write[i].right_mode = p_mode; if (i + 1 < _points.size()) { if (p_mode == TANGENT_LINEAR) { Vector2 v = (_points[i + 1].pos - _points[i].pos).normalized(); - _points[i].right_tangent = v.y / v.x; + _points.write[i].right_tangent = v.y / v.x; } } mark_dirty(); @@ -222,7 +222,7 @@ void Curve::clear_points() { void Curve::set_point_value(int p_index, real_t pos) { ERR_FAIL_INDEX(p_index, _points.size()); - _points[p_index].pos.y = pos; + _points.write[p_index].pos.y = pos; update_auto_tangents(p_index); mark_dirty(); } @@ -232,10 +232,10 @@ int Curve::set_point_offset(int p_index, float offset) { Point p = _points[p_index]; remove_point(p_index); int i = add_point(Vector2(offset, p.pos.y)); - _points[i].left_tangent = p.left_tangent; - _points[i].right_tangent = p.right_tangent; - _points[i].left_mode = p.left_mode; - _points[i].right_mode = p.right_mode; + _points.write[i].left_tangent = p.left_tangent; + _points.write[i].right_tangent = p.right_tangent; + _points.write[i].left_mode = p.left_mode; + _points.write[i].right_mode = p.right_mode; if (p_index != i) update_auto_tangents(p_index); update_auto_tangents(i); @@ -254,7 +254,7 @@ Curve::Point Curve::get_point(int p_index) const { void Curve::update_auto_tangents(int i) { - Point &p = _points[i]; + Point &p = _points.write[i]; if (i > 0) { if (p.left_mode == TANGENT_LINEAR) { @@ -263,7 +263,7 @@ void Curve::update_auto_tangents(int i) { } if (_points[i - 1].right_mode == TANGENT_LINEAR) { Vector2 v = (_points[i - 1].pos - p.pos).normalized(); - _points[i - 1].right_tangent = v.y / v.x; + _points.write[i - 1].right_tangent = v.y / v.x; } } @@ -274,7 +274,7 @@ void Curve::update_auto_tangents(int i) { } if (_points[i + 1].left_mode == TANGENT_LINEAR) { Vector2 v = (_points[i + 1].pos - p.pos).normalized(); - _points[i + 1].left_tangent = v.y / v.x; + _points.write[i + 1].left_tangent = v.y / v.x; } } } @@ -402,7 +402,7 @@ void Curve::set_data(Array input) { for (int j = 0; j < _points.size(); ++j) { - Point &p = _points[j]; + Point &p = _points.write[j]; int i = j * ELEMS; p.pos = input[i]; @@ -426,12 +426,12 @@ void Curve::bake() { for (int i = 1; i < _bake_resolution - 1; ++i) { real_t x = i / static_cast<real_t>(_bake_resolution); real_t y = interpolate(x); - _baked_cache[i] = y; + _baked_cache.write[i] = y; } if (_points.size() != 0) { - _baked_cache[0] = _points[0].pos.y; - _baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y; + _baked_cache.write[0] = _points[0].pos.y; + _baked_cache.write[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y; } _baked_cache_dirty = false; @@ -553,7 +553,7 @@ void Curve2D::set_point_position(int p_index, const Vector2 &p_pos) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].pos = p_pos; + points.write[p_index].pos = p_pos; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -567,7 +567,7 @@ void Curve2D::set_point_in(int p_index, const Vector2 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].in = p_in; + points.write[p_index].in = p_in; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -581,7 +581,7 @@ void Curve2D::set_point_out(int p_index, const Vector2 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].out = p_out; + points.write[p_index].out = p_out; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -930,9 +930,9 @@ void Curve2D::_set_data(const Dictionary &p_data) { for (int i = 0; i < points.size(); i++) { - points[i].in = r[i * 3 + 0]; - points[i].out = r[i * 3 + 1]; - points[i].pos = r[i * 3 + 2]; + points.write[i].in = r[i * 3 + 0]; + points.write[i].out = r[i * 3 + 1]; + points.write[i].pos = r[i * 3 + 2]; } baked_cache_dirty = true; @@ -952,7 +952,7 @@ PoolVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) const int pc = 1; for (int i = 0; i < points.size() - 1; i++) { - _bake_segment2d(midpoints[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); + _bake_segment2d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); pc++; pc += midpoints[i].size(); } @@ -1049,7 +1049,7 @@ void Curve3D::set_point_position(int p_index, const Vector3 &p_pos) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].pos = p_pos; + points.write[p_index].pos = p_pos; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1063,7 +1063,7 @@ void Curve3D::set_point_tilt(int p_index, float p_tilt) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].tilt = p_tilt; + points.write[p_index].tilt = p_tilt; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1077,7 +1077,7 @@ void Curve3D::set_point_in(int p_index, const Vector3 &p_in) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].in = p_in; + points.write[p_index].in = p_in; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1091,7 +1091,7 @@ void Curve3D::set_point_out(int p_index, const Vector3 &p_out) { ERR_FAIL_INDEX(p_index, points.size()); - points[p_index].out = p_out; + points.write[p_index].out = p_out; baked_cache_dirty = true; emit_signal(CoreStringNames::get_singleton()->changed); } @@ -1621,10 +1621,10 @@ void Curve3D::_set_data(const Dictionary &p_data) { for (int i = 0; i < points.size(); i++) { - points[i].in = r[i * 3 + 0]; - points[i].out = r[i * 3 + 1]; - points[i].pos = r[i * 3 + 2]; - points[i].tilt = rt[i]; + points.write[i].in = r[i * 3 + 0]; + points.write[i].out = r[i * 3 + 1]; + points.write[i].pos = r[i * 3 + 2]; + points.write[i].tilt = rt[i]; } baked_cache_dirty = true; @@ -1644,7 +1644,7 @@ PoolVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) const int pc = 1; for (int i = 0; i < points.size() - 1; i++) { - _bake_segment3d(midpoints[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); + _bake_segment3d(midpoints.write[i], 0, 1, points[i].pos, points[i].out, points[i + 1].pos, points[i + 1].in, 0, p_max_stages, p_tolerance); pc++; pc += midpoints[i].size(); } diff --git a/scene/resources/cylinder_shape.cpp b/scene/resources/cylinder_shape.cpp new file mode 100644 index 0000000000..f760462d49 --- /dev/null +++ b/scene/resources/cylinder_shape.cpp @@ -0,0 +1,116 @@ +/*************************************************************************/ +/* cylinder_shape.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "cylinder_shape.h" +#include "servers/physics_server.h" + +Vector<Vector3> CylinderShape::_gen_debug_mesh_lines() { + + float radius = get_radius(); + float height = get_height(); + + Vector<Vector3> points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + return points; +} + +void CylinderShape::_update_shape() { + + Dictionary d; + d["radius"] = radius; + d["height"] = height; + PhysicsServer::get_singleton()->shape_set_data(get_shape(), d); +} + +void CylinderShape::set_radius(float p_radius) { + + radius = p_radius; + _update_shape(); + notify_change_to_owners(); + _change_notify("radius"); +} + +float CylinderShape::get_radius() const { + + return radius; +} + +void CylinderShape::set_height(float p_height) { + + height = p_height; + _update_shape(); + notify_change_to_owners(); + _change_notify("height"); +} + +float CylinderShape::get_height() const { + + return height; +} + +void CylinderShape::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CylinderShape::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &CylinderShape::get_radius); + ClassDB::bind_method(D_METHOD("set_height", "height"), &CylinderShape::set_height); + ClassDB::bind_method(D_METHOD("get_height"), &CylinderShape::get_height); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height"); +} + +CylinderShape::CylinderShape() : + Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CYLINDER)) { + + radius = 1.0; + height = 2.0; + _update_shape(); +} diff --git a/scene/resources/cylinder_shape.h b/scene/resources/cylinder_shape.h new file mode 100644 index 0000000000..f510758e91 --- /dev/null +++ b/scene/resources/cylinder_shape.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* cylinder_shape.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CYLINDER_SHAPE_H +#define CYLINDER_SHAPE_H + +#include "scene/resources/shape.h" + +class CylinderShape : public Shape { + + GDCLASS(CylinderShape, Shape); + float radius; + float height; + +protected: + static void _bind_methods(); + virtual void _update_shape(); + + virtual Vector<Vector3> _gen_debug_mesh_lines(); + +public: + void set_radius(float p_radius); + float get_radius() const; + void set_height(float p_height); + float get_height() const; + + CylinderShape(); +}; + +#endif // CYLINDER_SHAPE_H diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 702953fa40..601f6fb558 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -476,6 +476,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("symbol_color", "TextEdit", control_font_color_hover); theme->set_color("brace_mismatch_color", "TextEdit", Color(1, 0.2, 0.2)); theme->set_color("line_number_color", "TextEdit", Color::html("66aaaaaa")); + theme->set_color("safe_line_number_color", "TextEdit", Color::html("99aac8aa")); theme->set_color("function_color", "TextEdit", Color::html("66a2ce")); theme->set_color("member_variable_color", "TextEdit", Color::html("e64e59")); theme->set_color("number_color", "TextEdit", Color::html("EB9532")); @@ -585,6 +586,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("panel_disabled", "PopupMenu", make_stylebox(popup_bg_disabled_png, 4, 4, 4, 4)); theme->set_stylebox("hover", "PopupMenu", selected); theme->set_stylebox("separator", "PopupMenu", make_stylebox(vseparator_png, 3, 3, 3, 3)); + theme->set_stylebox("labeled_separator_left", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0)); + theme->set_stylebox("labeled_separator_right", "PopupMenu", make_stylebox(vseparator_png, 0, 0, 0, 0)); theme->set_icon("checked", "PopupMenu", make_icon(checked_png)); theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png)); @@ -880,6 +883,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_icon("logo", "Icons", make_icon(logo_png)); + // Visual Node Ports + theme->set_constant("port_grab_distance_horizontal", "GraphEdit", 48 * scale); + theme->set_constant("port_grab_distance_vertical", "GraphEdit", 6 * scale); + // Theme default_icon = make_icon(error_icon_png); diff --git a/scene/resources/default_theme/make_header.py b/scene/resources/default_theme/make_header.py index db449f9417..73b1ae0b0b 100755 --- a/scene/resources/default_theme/make_header.py +++ b/scene/resources/default_theme/make_header.py @@ -4,15 +4,16 @@ import os import glob import string +enc = "utf-8" # Generate include files f = open("theme_data.h", "wb") -f.write("// THIS FILE HAS BEEN AUTOGENERATED, DON'T EDIT!!\n") +f.write(b"// THIS FILE HAS BEEN AUTOGENERATED, DON\'T EDIT!!\n") # Generate png image block -f.write("\n// png image block\n"); +f.write(b"\n// png image block\n") pixmaps = glob.glob("*.png") pixmaps.sort() @@ -21,22 +22,23 @@ for x in pixmaps: var_str = x[:-4] + "_png" - f.write("\nstatic const unsigned char " + var_str + "[] = {\n\t") + s = "\nstatic const unsigned char " + var_str + "[] = {\n\t" + f.write(s.encode(enc)) pngf = open(x, "rb") b = pngf.read(1) while(len(b) == 1): - f.write(hex(ord(b))) + f.write(hex(ord(b)).encode(enc)) b = pngf.read(1) if (len(b) == 1): - f.write(", ") + f.write(b", ") - f.write("\n};\n") + f.write(b"\n};\n") pngf.close() # Generate shaders block -f.write("\n// shaders block\n"); +f.write(b"\n// shaders block\n"); shaders = glob.glob("*.gsl") shaders.sort() @@ -45,7 +47,8 @@ for x in shaders: var_str = x[:-4] + "_shader_code" - f.write("\nstatic const char *" + var_str + " = \n") + s = "\nstatic const char *" + var_str + " = \n" + f.write(s.encode(enc)) sf = open(x, "rb") @@ -55,12 +58,13 @@ for x in shaders: b = b[:-2] if (b.endswith("\n")): b = b[:-1] - f.write(" \"" + b) + s = ' \"' + b + f.write(s.encode(enc)) b = sf.readline() if (b != ""): - f.write("\"\n") + f.write(b'"\n') - f.write("\";\n") + f.write(b'";\n') sf.close() f.close() diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index e5d463d391..2f2abd4e08 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -211,9 +211,9 @@ Error DynamicFontAtSize::_load() { scale_color_font = float(id.size) / face->available_sizes[i].width; } } - error = FT_Select_Size(face, best_match); + FT_Select_Size(face, best_match); } else { - error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); + FT_Set_Pixel_Sizes(face, 0, id.size * oversampling); } ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font; @@ -314,7 +314,7 @@ void DynamicFontAtSize::set_texture_flags(uint32_t p_flags) { texture_flags = p_flags; for (int i = 0; i < textures.size(); i++) { - Ref<ImageTexture> &tex = textures[i].texture; + Ref<ImageTexture> &tex = textures.write[i].texture; if (!tex.is_null()) tex->set_flags(p_flags); } @@ -400,7 +400,7 @@ DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyp for (int i = 0; i < textures.size(); i++) { - CharTexture &ct = textures[i]; + const CharTexture &ct = textures[i]; if (ct.texture->get_format() != p_image_format) continue; @@ -466,7 +466,7 @@ DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyp } tex.offsets.resize(texsize); for (int i = 0; i < texsize; i++) //zero offsets - tex.offsets[i] = 0; + tex.offsets.write[i] = 0; textures.push_back(tex); ret.index = textures.size() - 1; @@ -493,7 +493,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap b //fit character in char texture - CharTexture &tex = textures[tex_pos.index]; + CharTexture &tex = textures.write[tex_pos.index]; { PoolVector<uint8_t>::Write wr = tex.imgdata.write(); @@ -547,7 +547,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap b // update height array for (int k = tex_pos.x; k < tex_pos.x + mw; k++) { - tex.offsets[k] = tex_pos.y + mh; + tex.offsets.write[k] = tex_pos.y + mh; } Character chr; @@ -698,9 +698,9 @@ void DynamicFont::_reload_cache() { } for (int i = 0; i < fallbacks.size(); i++) { - fallback_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(cache_id); + fallback_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(cache_id); if (outline_cache_id.outline_size > 0) - fallback_outline_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(outline_cache_id); + fallback_outline_data_at_size.write[i] = fallbacks.write[i]->_get_dynamic_font_at_size(outline_cache_id); } emit_changed(); @@ -895,17 +895,17 @@ void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); ERR_FAIL_INDEX(p_idx, fallbacks.size()); - fallbacks[p_idx] = p_data; - fallback_data_at_size[p_idx] = fallbacks[p_idx]->_get_dynamic_font_at_size(cache_id); + fallbacks.write[p_idx] = p_data; + fallback_data_at_size.write[p_idx] = fallbacks.write[p_idx]->_get_dynamic_font_at_size(cache_id); } void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) { ERR_FAIL_COND(p_data.is_null()); fallbacks.push_back(p_data); - fallback_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. + fallback_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const.. if (outline_cache_id.outline_size > 0) - fallback_outline_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); + fallback_outline_data_at_size.push_back(fallbacks.write[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id)); _change_notify(); emit_changed(); @@ -1092,7 +1092,7 @@ void DynamicFont::update_oversampling() { dynamic_font_mutex->unlock(); for (int i = 0; i < changed.size(); i++) { - changed[i]->emit_changed(); + changed.write[i]->emit_changed(); } } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 0bae9d9b2d..3dfde01320 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -386,7 +386,7 @@ Vector<CharType> BitmapFont::get_char_keys() const { int count = 0; while ((ct = char_map.next(ct))) { - chars[count++] = *ct; + chars.write[count++] = *ct; }; return chars; @@ -438,7 +438,7 @@ Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const { int i = 0; for (Map<KerningPairKey, int>::Element *E = kerning_map.front(); E; E = E->next()) { - ret[i++] = E->key(); + ret.write[i++] = E->key(); } return ret; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 654d7b884e..875b72159a 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -145,11 +145,17 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { void ShaderMaterial::set_shader(const Ref<Shader> &p_shader) { + if (shader.is_valid()) { + shader->disconnect("changed", this, "_shader_changed"); + } + shader = p_shader; RID rid; - if (shader.is_valid()) + if (shader.is_valid()) { rid = shader->get_rid(); + shader->connect("changed", this, "_shader_changed"); + } VS::get_singleton()->material_set_shader(_get_material(), rid); _change_notify(); //properties for shader exposed @@ -171,12 +177,17 @@ Variant ShaderMaterial::get_shader_param(const StringName &p_param) const { return VS::get_singleton()->material_get_param(_get_material(), p_param); } +void ShaderMaterial::_shader_changed() { + _change_notify(); //update all properties +} + void ShaderMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shader", "shader"), &ShaderMaterial::set_shader); ClassDB::bind_method(D_METHOD("get_shader"), &ShaderMaterial::get_shader); ClassDB::bind_method(D_METHOD("set_shader_param", "param", "value"), &ShaderMaterial::set_shader_param); ClassDB::bind_method(D_METHOD("get_shader_param", "param"), &ShaderMaterial::get_shader_param); + ClassDB::bind_method(D_METHOD("_shader_changed"), &ShaderMaterial::_shader_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shader", PROPERTY_HINT_RESOURCE_TYPE, "Shader"), "set_shader", "get_shader"); } @@ -393,6 +404,9 @@ void SpatialMaterial::_update_shader() { if (flags[FLAG_DONT_RECEIVE_SHADOWS]) { code += ",shadows_disabled"; } + if (flags[FLAG_DISABLE_AMBIENT_LIGHT]) { + code += ",ambient_light_disabled"; + } if (flags[FLAG_ENSURE_CORRECT_NORMALS]) { code += ",ensure_correct_normals"; } @@ -540,9 +554,20 @@ void SpatialMaterial::_update_shader() { case BILLBOARD_ENABLED: { code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],CAMERA_MATRIX[1],CAMERA_MATRIX[2],WORLD_MATRIX[3]);\n"; + + if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz),0,0,0),vec4(0,length(WORLD_MATRIX[1].xyz),0,0),vec4(0,0,length(WORLD_MATRIX[2].xyz),0),vec4(0,0,0,1));\n"; + } } break; case BILLBOARD_FIXED_Y: { + code += "\tMODELVIEW_MATRIX = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0],WORLD_MATRIX[1],vec4(normalize(cross(CAMERA_MATRIX[0].xyz,WORLD_MATRIX[1].xyz)),0.0),WORLD_MATRIX[3]);\n"; + + if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(WORLD_MATRIX[0].xyz),0,0,0),vec4(0,1,0,0),vec4(0,0,length(WORLD_MATRIX[2].xyz),0),vec4(0,0,0,1));\n"; + } else { + code += "\tMODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(1,0,0,0),vec4(0,1.0/length(WORLD_MATRIX[1].xyz),0,0),vec4(0,0,1,0),vec4(0,0,0,1));\n"; + } } break; case BILLBOARD_PARTICLES: { @@ -559,8 +584,6 @@ void SpatialMaterial::_update_shader() { code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n"; code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n"; code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n"; - //handle rotation - // code += "\tmat4 rotation = mat4(" } break; } @@ -1855,6 +1878,7 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_ensure_correct_normals"), "set_flag", "get_flag", FLAG_ENSURE_CORRECT_NORMALS); ADD_GROUP("Vertex Color", "vertex_color"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR); @@ -1868,7 +1892,8 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "params_depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never,Opaque Pre-Pass"), "set_depth_draw_mode", "get_depth_draw_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_line_width", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_line_width", "get_line_width"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_point_size", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_point_size", "get_point_size"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "params_billboard_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particle Billboard"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "params_billboard_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particle Billboard1"), "set_billboard_mode", "get_billboard_mode"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_billboard_keep_scale"), "set_flag", "get_flag", FLAG_BILLBOARD_KEEP_SCALE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "params_grow"), "set_grow_enabled", "is_grow_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "params_grow_amount", PROPERTY_HINT_RANGE, "-16,10,0.01"), "set_grow", "get_grow"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "params_use_alpha_scissor"), "set_flag", "get_flag", FLAG_USE_ALPHA_SCISSOR); @@ -2038,6 +2063,7 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_SRGB_VERTEX_COLOR); BIND_ENUM_CONSTANT(FLAG_USE_POINT_SIZE); BIND_ENUM_CONSTANT(FLAG_FIXED_SIZE); + BIND_ENUM_CONSTANT(FLAG_BILLBOARD_KEEP_SCALE); BIND_ENUM_CONSTANT(FLAG_UV1_USE_TRIPLANAR); BIND_ENUM_CONSTANT(FLAG_UV2_USE_TRIPLANAR); BIND_ENUM_CONSTANT(FLAG_AO_ON_UV2); @@ -2046,6 +2072,7 @@ void SpatialMaterial::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD); BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB); BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS); + BIND_ENUM_CONSTANT(FLAG_DISABLE_AMBIENT_LIGHT); BIND_ENUM_CONSTANT(FLAG_ENSURE_CORRECT_NORMALS); BIND_ENUM_CONSTANT(FLAG_MAX); @@ -2079,10 +2106,10 @@ void SpatialMaterial::_bind_methods() { SpatialMaterial::SpatialMaterial() : element(this) { - //initialize to right values + // Initialize to the same values as the shader set_albedo(Color(1.0, 1.0, 1.0, 1.0)); set_specular(0.5); - set_roughness(0.0); + set_roughness(1.0); set_metallic(0.0); set_emission(Color(0, 0, 0)); set_emission_energy(1.0); diff --git a/scene/resources/material.h b/scene/resources/material.h index 87594213bc..7a1a4acfbf 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -92,6 +92,8 @@ protected: virtual bool _can_do_next_pass() const; + void _shader_changed(); + public: void set_shader(const Ref<Shader> &p_shader); Ref<Shader> get_shader() const; @@ -181,6 +183,7 @@ public: FLAG_SRGB_VERTEX_COLOR, FLAG_USE_POINT_SIZE, FLAG_FIXED_SIZE, + FLAG_BILLBOARD_KEEP_SCALE, FLAG_UV1_USE_TRIPLANAR, FLAG_UV2_USE_TRIPLANAR, FLAG_TRIPLANAR_USE_WORLD, @@ -190,6 +193,7 @@ public: FLAG_ALBEDO_TEXTURE_FORCE_SRGB, FLAG_DONT_RECEIVE_SHADOWS, FLAG_ENSURE_CORRECT_NORMALS, + FLAG_DISABLE_AMBIENT_LIGHT, FLAG_MAX }; @@ -238,7 +242,7 @@ private: uint64_t blend_mode : 2; uint64_t depth_draw_mode : 2; uint64_t cull_mode : 2; - uint64_t flags : 16; + uint64_t flags : 18; uint64_t detail_blend_mode : 2; uint64_t diffuse_mode : 3; uint64_t specular_mode : 2; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index b0620d3363..4e6004709e 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -40,7 +40,6 @@ void Mesh::_clear_triangle_mesh() const { triangle_mesh.unref(); - ; } Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { @@ -110,6 +109,54 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { return triangle_mesh; } +void Mesh::generate_debug_mesh_lines(Vector<Vector3> &r_lines) { + + Ref<TriangleMesh> tm = generate_triangle_mesh(); + if (tm.is_null()) + return; + + PoolVector<int> triangle_indices; + tm->get_indices(&triangle_indices); + const int triangles_num = tm->get_triangles().size(); + PoolVector<Vector3> vertices = tm->get_vertices(); + + r_lines.resize(tm->get_triangles().size() * 6); // 3 lines x 2 points each line + + PoolVector<int>::Read ind_r = triangle_indices.read(); + PoolVector<Vector3>::Read ver_r = vertices.read(); + for (int j = 0, x = 0, i = 0; i < triangles_num; j += 6, x += 3, ++i) { + // Triangle line 1 + r_lines.write[j + 0] = ver_r[ind_r[x + 0]]; + r_lines.write[j + 1] = ver_r[ind_r[x + 1]]; + + // Triangle line 2 + r_lines.write[j + 2] = ver_r[ind_r[x + 1]]; + r_lines.write[j + 3] = ver_r[ind_r[x + 2]]; + + // Triangle line 3 + r_lines.write[j + 4] = ver_r[ind_r[x + 2]]; + r_lines.write[j + 5] = ver_r[ind_r[x + 0]]; + } +} +void Mesh::generate_debug_mesh_indices(Vector<Vector3> &r_points) { + Ref<TriangleMesh> tm = generate_triangle_mesh(); + if (tm.is_null()) + return; + + PoolVector<Vector3> vertices = tm->get_vertices(); + + int vertices_size = vertices.size(); + r_points.resize(vertices_size); + for (int i = 0; i < vertices_size; ++i) { + r_points.write[i] = vertices[i]; + } +} + +bool Mesh::surface_is_softbody_friendly(int p_idx) const { + const uint32_t surface_format = surface_get_format(p_idx); + return (surface_format & Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE && (!(surface_format & Mesh::ARRAY_COMPRESS_VERTEX)) && (!(surface_format & Mesh::ARRAY_COMPRESS_NORMAL))); +} + PoolVector<Face3> Mesh::get_faces() const { Ref<TriangleMesh> tm = generate_triangle_mesh(); @@ -484,6 +531,10 @@ void Mesh::_bind_methods() { BIND_ENUM_CONSTANT(ARRAY_MAX); } +void Mesh::clear_cache() { + _clear_triangle_mesh(); +} + Mesh::Mesh() { } @@ -616,7 +667,7 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { bone_aabb.resize(baabb.size()); for (int i = 0; i < baabb.size(); i++) { - bone_aabb[i] = baabb[i]; + bone_aabb.write[i] = baabb[i]; } } @@ -788,8 +839,8 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array & aabb.expand_to(vtx[i]); } - surfaces[surfaces.size() - 1].aabb = aabb; - surfaces[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY; + surfaces.write[surfaces.size() - 1].aabb = aabb; + surfaces.write[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY; _recompute_aabb(); } @@ -908,18 +959,28 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) ERR_FAIL_INDEX(p_idx, surfaces.size()); if (surfaces[p_idx].material == p_material) return; - surfaces[p_idx].material = p_material; + surfaces.write[p_idx].material = p_material; VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid()); _change_notify("material"); emit_changed(); } +int ArrayMesh::surface_find_by_name(const String &p_name) const { + for (int i = 0; i < surfaces.size(); i++) { + if (surfaces[i].name == p_name) { + return i; + } + } + + return -1; +} + void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { ERR_FAIL_INDEX(p_idx, surfaces.size()); - surfaces[p_idx].name = p_name; + surfaces.write[p_idx].name = p_name; emit_changed(); } @@ -939,7 +1000,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { ERR_FAIL_INDEX(p_idx, surfaces.size()); - surfaces[p_idx].aabb = p_aabb; + surfaces.write[p_idx].aabb = p_aabb; // set custom aabb too? emit_changed(); } @@ -1042,8 +1103,8 @@ void ArrayMesh::regen_normalmaps() { for (int i = 0; i < surfs.size(); i++) { - surfs[i]->generate_tangents(); - surfs[i]->commit(Ref<ArrayMesh>(this)); + surfs.write[i]->generate_tangents(); + surfs.write[i]->commit(Ref<ArrayMesh>(this)); } } @@ -1108,13 +1169,13 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe Vector3 v = p_base_transform.xform(r[j]); Vector3 n = p_base_transform.basis.xform(rn[j]).normalized(); - vertices[(j + vertex_ofs) * 3 + 0] = v.x; - vertices[(j + vertex_ofs) * 3 + 1] = v.y; - vertices[(j + vertex_ofs) * 3 + 2] = v.z; - normals[(j + vertex_ofs) * 3 + 0] = n.x; - normals[(j + vertex_ofs) * 3 + 1] = n.y; - normals[(j + vertex_ofs) * 3 + 2] = n.z; - uv_index[j + vertex_ofs] = Pair<int, int>(i, j); + vertices.write[(j + vertex_ofs) * 3 + 0] = v.x; + vertices.write[(j + vertex_ofs) * 3 + 1] = v.y; + vertices.write[(j + vertex_ofs) * 3 + 2] = v.z; + normals.write[(j + vertex_ofs) * 3 + 0] = n.x; + normals.write[(j + vertex_ofs) * 3 + 1] = n.y; + normals.write[(j + vertex_ofs) * 3 + 2] = n.z; + uv_index.write[j + vertex_ofs] = Pair<int, int>(i, j); } PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX]; @@ -1197,31 +1258,31 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second]; if (surfaces[surface].format & ARRAY_FORMAT_COLOR) { - surfaces_tools[surface]->add_color(v.color); + surfaces_tools.write[surface]->add_color(v.color); } if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) { - surfaces_tools[surface]->add_uv(v.uv); + surfaces_tools.write[surface]->add_uv(v.uv); } if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) { - surfaces_tools[surface]->add_normal(v.normal); + surfaces_tools.write[surface]->add_normal(v.normal); } if (surfaces[surface].format & ARRAY_FORMAT_TANGENT) { Plane t; t.normal = v.tangent; t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1; - surfaces_tools[surface]->add_tangent(t); + surfaces_tools.write[surface]->add_tangent(t); } if (surfaces[surface].format & ARRAY_FORMAT_BONES) { - surfaces_tools[surface]->add_bones(v.bones); + surfaces_tools.write[surface]->add_bones(v.bones); } if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) { - surfaces_tools[surface]->add_weights(v.weights); + surfaces_tools.write[surface]->add_weights(v.weights); } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); - surfaces_tools[surface]->add_uv2(uv2); + surfaces_tools.write[surface]->add_uv2(uv2); - surfaces_tools[surface]->add_vertex(v.vertex); + surfaces_tools.write[surface]->add_vertex(v.vertex); } } @@ -1233,8 +1294,8 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe //generate surfaces for (int i = 0; i < surfaces_tools.size(); i++) { - surfaces_tools[i]->index(); - surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format); + surfaces_tools.write[i]->index(); + surfaces_tools.write[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); @@ -1261,6 +1322,7 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_get_primitive_type", "surf_idx"), &ArrayMesh::surface_get_primitive_type); ClassDB::bind_method(D_METHOD("surface_set_material", "surf_idx", "material"), &ArrayMesh::surface_set_material); ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &ArrayMesh::surface_get_material); + ClassDB::bind_method(D_METHOD("surface_find_by_name", "name"), &ArrayMesh::surface_find_by_name); ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &ArrayMesh::surface_get_arrays); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index a3fb068569..36bfca49f8 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -123,6 +123,7 @@ public: virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; + virtual bool surface_is_softbody_friendly(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const = 0; virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual uint32_t surface_get_format(int p_idx) const = 0; @@ -133,6 +134,8 @@ public: PoolVector<Face3> get_faces() const; Ref<TriangleMesh> generate_triangle_mesh() const; + void generate_debug_mesh_lines(Vector<Vector3> &r_lines); + void generate_debug_mesh_indices(Vector<Vector3> &r_points); Ref<Shape> create_trimesh_shape() const; Ref<Shape> create_convex_shape() const; @@ -143,6 +146,7 @@ public: void set_lightmap_size_hint(const Vector2 &p_size); Size2 get_lightmap_size_hint() const; + void clear_cache(); Mesh(); }; @@ -208,6 +212,7 @@ public: void surface_set_material(int p_idx, const Ref<Material> &p_material); Ref<Material> surface_get_material(int p_idx) const; + int surface_find_by_name(const String &p_name) const; void surface_set_name(int p_idx, const String &p_name); String surface_get_name(int p_idx) const; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 8639b325c3..6732303925 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -120,7 +120,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf v.bones.push_back(bo[i * 4 + 3]); } - vertices[i] = v; + vertices.write[i] = v; } PoolVector<int> indices; @@ -143,7 +143,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf for (int i = 0; i < icount; i += 3) { - Vertex *v[3] = { &vertices[r[i + 0]], &vertices[r[i + 1]], &vertices[r[i + 2]] }; + Vertex *v[3] = { &vertices.write[r[i + 0]], &vertices.write[r[i + 1]], &vertices.write[r[i + 2]] }; int fidx = faces.size(); Face face; @@ -169,7 +169,7 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf edges.push_back(e); } - edges[face.edges[j]].faces.push_back(fidx); + edges.write[face.edges[j]].faces.push_back(fidx); v[j]->faces.push_back(fidx); v[j]->edges.push_back(face.edges[j]); } @@ -247,7 +247,7 @@ Error MeshDataTool::commit_to_surface(const Ref<ArrayMesh> &p_mesh) { for (int i = 0; i < vcount; i++) { - Vertex &vtx = vertices[i]; + const Vertex &vtx = vertices[i]; vr[i] = vtx.vertex; if (nr.ptr()) @@ -344,7 +344,7 @@ Vector3 MeshDataTool::get_vertex(int p_idx) const { void MeshDataTool::set_vertex(int p_idx, const Vector3 &p_vertex) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].vertex = p_vertex; + vertices.write[p_idx].vertex = p_vertex; } Vector3 MeshDataTool::get_vertex_normal(int p_idx) const { @@ -355,7 +355,7 @@ Vector3 MeshDataTool::get_vertex_normal(int p_idx) const { void MeshDataTool::set_vertex_normal(int p_idx, const Vector3 &p_normal) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].normal = p_normal; + vertices.write[p_idx].normal = p_normal; format |= Mesh::ARRAY_FORMAT_NORMAL; } @@ -367,7 +367,7 @@ Plane MeshDataTool::get_vertex_tangent(int p_idx) const { void MeshDataTool::set_vertex_tangent(int p_idx, const Plane &p_tangent) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].tangent = p_tangent; + vertices.write[p_idx].tangent = p_tangent; format |= Mesh::ARRAY_FORMAT_TANGENT; } @@ -379,7 +379,7 @@ Vector2 MeshDataTool::get_vertex_uv(int p_idx) const { void MeshDataTool::set_vertex_uv(int p_idx, const Vector2 &p_uv) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].uv = p_uv; + vertices.write[p_idx].uv = p_uv; format |= Mesh::ARRAY_FORMAT_TEX_UV; } @@ -391,7 +391,7 @@ Vector2 MeshDataTool::get_vertex_uv2(int p_idx) const { void MeshDataTool::set_vertex_uv2(int p_idx, const Vector2 &p_uv2) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].uv2 = p_uv2; + vertices.write[p_idx].uv2 = p_uv2; format |= Mesh::ARRAY_FORMAT_TEX_UV2; } @@ -403,7 +403,7 @@ Color MeshDataTool::get_vertex_color(int p_idx) const { void MeshDataTool::set_vertex_color(int p_idx, const Color &p_color) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].color = p_color; + vertices.write[p_idx].color = p_color; format |= Mesh::ARRAY_FORMAT_COLOR; } @@ -415,7 +415,7 @@ Vector<int> MeshDataTool::get_vertex_bones(int p_idx) const { void MeshDataTool::set_vertex_bones(int p_idx, const Vector<int> &p_bones) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].bones = p_bones; + vertices.write[p_idx].bones = p_bones; format |= Mesh::ARRAY_FORMAT_BONES; } @@ -426,7 +426,7 @@ Vector<float> MeshDataTool::get_vertex_weights(int p_idx) const { } void MeshDataTool::set_vertex_weights(int p_idx, const Vector<float> &p_weights) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].weights = p_weights; + vertices.write[p_idx].weights = p_weights; format |= Mesh::ARRAY_FORMAT_WEIGHTS; } @@ -439,7 +439,7 @@ Variant MeshDataTool::get_vertex_meta(int p_idx) const { void MeshDataTool::set_vertex_meta(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, vertices.size()); - vertices[p_idx].meta = p_meta; + vertices.write[p_idx].meta = p_meta; } Vector<int> MeshDataTool::get_vertex_edges(int p_idx) const { @@ -472,7 +472,7 @@ Variant MeshDataTool::get_edge_meta(int p_idx) const { void MeshDataTool::set_edge_meta(int p_idx, const Variant &p_meta) { ERR_FAIL_INDEX(p_idx, edges.size()); - edges[p_idx].meta = p_meta; + edges.write[p_idx].meta = p_meta; } int MeshDataTool::get_face_vertex(int p_face, int p_vertex) const { @@ -495,7 +495,7 @@ Variant MeshDataTool::get_face_meta(int p_face) const { void MeshDataTool::set_face_meta(int p_face, const Variant &p_meta) { ERR_FAIL_INDEX(p_face, faces.size()); - faces[p_face].meta = p_meta; + faces.write[p_face].meta = p_meta; } Vector3 MeshDataTool::get_face_normal(int p_face) const { diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index e1d3540fd1..a1d3e0ba1e 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -209,7 +209,7 @@ Vector<int> MeshLibrary::get_item_list() const { int idx = 0; for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) { - ret[idx++] = E->key(); + ret.write[idx++] = E->key(); } return ret; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 846f6e356e..07783d5f4a 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -31,6 +31,7 @@ #include "packed_scene.h" #include "core/core_string_names.h" +#include "engine.h" #include "io/resource_loader.h" #include "project_settings.h" #include "scene/2d/node_2d.h" @@ -279,7 +280,12 @@ Node *SceneState::instance(GenEditState p_edit_state) const { stray_instances.push_back(node); //can't be added, go to stray list } } else { - node->_set_name_nocheck(snames[n.name]); + if (Engine::get_singleton()->is_editor_hint()) { + //validate name if using editor, to avoid broken + node->set_name(snames[n.name]); + } else { + node->_set_name_nocheck(snames[n.name]); + } } } @@ -325,7 +331,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const { if (c.binds.size()) { binds.resize(c.binds.size()); for (int j = 0; j < c.binds.size(); j++) - binds[j] = props[c.binds[j]]; + binds.write[j] = props[c.binds[j]]; } cfrom->connect(snames[c.signal], cto, snames[c.method], binds, CONNECT_PERSIST | c.flags); @@ -389,7 +395,15 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.name = _nm_get_string(p_node->get_name(), name_map); nd.instance = -1; //not instanced by default - nd.index = p_node->get_index(); + + //really convoluted condition, but it basically checks that index is only saved when part of an inherited scene OR the node parent is from the edited scene + if (p_owner->get_scene_inherited_state().is_null() && (p_node == p_owner || (p_node->get_owner() == p_owner && (p_node->get_parent() == p_owner || p_node->get_parent()->get_owner() == p_owner)))) { + //do not save index, because it belongs to saved scene and scene is not inherited + nd.index = -1; + } else { + //part of an inherited scene, or parent is from an instanced scene + nd.index = p_node->get_index(); + } // if this node is part of an instanced scene or sub-instanced scene // we need to get the corresponding instance states. @@ -875,7 +889,7 @@ Error SceneState::pack(Node *p_scene) { for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) { - names[E->get()] = E->key(); + names.write[E->get()] = E->key(); } variants.resize(variant_map.size()); @@ -883,13 +897,13 @@ Error SceneState::pack(Node *p_scene) { while ((K = variant_map.next(K))) { int idx = variant_map[*K]; - variants[idx] = *K; + variants.write[idx] = *K; } node_paths.resize(nodepath_map.size()); for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) { - node_paths[E->get()] = scene->get_path_to(E->key()); + node_paths.write[E->get()] = scene->get_path_to(E->key()); } return OK; @@ -1090,7 +1104,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { names.resize(namecount); PoolVector<String>::Read r = snames.read(); for (int i = 0; i < names.size(); i++) - names[i] = r[i]; + names.write[i] = r[i]; } Array svariants = p_dictionary["variants"]; @@ -1100,7 +1114,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { variants.resize(varcount); for (int i = 0; i < varcount; i++) { - variants[i] = svariants[i]; + variants.write[i] = svariants[i]; } } else { @@ -1114,7 +1128,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { PoolVector<int>::Read r = snodes.read(); int idx = 0; for (int i = 0; i < nc; i++) { - NodeData &nd = nodes[i]; + NodeData &nd = nodes.write[i]; nd.parent = r[idx++]; nd.owner = r[idx++]; nd.type = r[idx++]; @@ -1126,13 +1140,13 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { nd.properties.resize(r[idx++]); for (int j = 0; j < nd.properties.size(); j++) { - nd.properties[j].name = r[idx++]; - nd.properties[j].value = r[idx++]; + nd.properties.write[j].name = r[idx++]; + nd.properties.write[j].value = r[idx++]; } nd.groups.resize(r[idx++]); for (int j = 0; j < nd.groups.size(); j++) { - nd.groups[j] = r[idx++]; + nd.groups.write[j] = r[idx++]; } } } @@ -1146,7 +1160,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { PoolVector<int>::Read r = sconns.read(); int idx = 0; for (int i = 0; i < cc; i++) { - ConnectionData &cd = connections[i]; + ConnectionData &cd = connections.write[i]; cd.from = r[idx++]; cd.to = r[idx++]; cd.signal = r[idx++]; @@ -1156,7 +1170,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { for (int j = 0; j < cd.binds.size(); j++) { - cd.binds[j] = r[idx++]; + cd.binds.write[j] = r[idx++]; } } } @@ -1167,7 +1181,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { } node_paths.resize(np.size()); for (int i = 0; i < np.size(); i++) { - node_paths[i] = np[i]; + node_paths.write[i] = np[i]; } Array ei; @@ -1181,7 +1195,7 @@ void SceneState::set_bundled_scene(const Dictionary &p_dictionary) { editable_instances.resize(ei.size()); for (int i = 0; i < editable_instances.size(); i++) { - editable_instances[i] = ei[i]; + editable_instances.write[i] = ei[i]; } //path=p_dictionary["path"]; @@ -1563,13 +1577,13 @@ void SceneState::add_node_property(int p_node, int p_name, int p_value) { NodeData::Property prop; prop.name = p_name; prop.value = p_value; - nodes[p_node].properties.push_back(prop); + nodes.write[p_node].properties.push_back(prop); } void SceneState::add_node_group(int p_node, int p_group) { ERR_FAIL_INDEX(p_node, nodes.size()); ERR_FAIL_INDEX(p_group, names.size()); - nodes[p_node].groups.push_back(p_group); + nodes.write[p_node].groups.push_back(p_group); } void SceneState::set_base_scene(int p_idx) { diff --git a/scene/resources/physics_material.cpp b/scene/resources/physics_material.cpp new file mode 100644 index 0000000000..dc5ca1aef6 --- /dev/null +++ b/scene/resources/physics_material.cpp @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* physics_material.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "physics_material.h" + +void PhysicsMaterial::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicsMaterial::set_friction); + ClassDB::bind_method(D_METHOD("get_friction"), &PhysicsMaterial::get_friction); + + ClassDB::bind_method(D_METHOD("set_rough", "rough"), &PhysicsMaterial::set_rough); + ClassDB::bind_method(D_METHOD("is_rough"), &PhysicsMaterial::is_rough); + + ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicsMaterial::set_bounce); + ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicsMaterial::get_bounce); + + ClassDB::bind_method(D_METHOD("set_absorbent", "absorbent"), &PhysicsMaterial::set_absorbent); + ClassDB::bind_method(D_METHOD("is_absorbent"), &PhysicsMaterial::is_absorbent); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction"), "set_friction", "get_friction"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rough"), "set_rough", "is_rough"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce"), "set_bounce", "get_bounce"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "absorbent"), "set_absorbent", "is_absorbent"); +} + +void PhysicsMaterial::set_friction(real_t p_val) { + friction = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_rough(bool p_val) { + rough = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_bounce(real_t p_val) { + bounce = p_val; + emit_changed(); +} + +void PhysicsMaterial::set_absorbent(bool p_val) { + absorbent = p_val; + emit_changed(); +} + +PhysicsMaterial::PhysicsMaterial() : + friction(1), + rough(false), + bounce(0), + absorbent(false) {} diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h new file mode 100644 index 0000000000..dfe48d94cf --- /dev/null +++ b/scene/resources/physics_material.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* physics_material.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef physics_material_override_H +#define physics_material_override_H + +#include "resource.h" +#include "servers/physics_server.h" + +class PhysicsMaterial : public Resource { + + GDCLASS(PhysicsMaterial, Resource); + OBJ_SAVE_TYPE(PhysicsMaterial); + RES_BASE_EXTENSION("PhyMat"); + + real_t friction; + bool rough; + real_t bounce; + bool absorbent; + +protected: + static void _bind_methods(); + +public: + void set_friction(real_t p_val); + _FORCE_INLINE_ real_t get_friction() const { return friction; } + + void set_rough(bool p_val); + _FORCE_INLINE_ bool is_rough() const { return rough; } + + _FORCE_INLINE_ real_t computed_friction() const { + return rough ? -friction : friction; + } + + void set_bounce(real_t p_val); + _FORCE_INLINE_ real_t get_bounce() const { return bounce; } + + void set_absorbent(bool p_val); + _FORCE_INLINE_ bool is_absorbent() const { return absorbent; } + + _FORCE_INLINE_ real_t computed_bounce() const { + return absorbent ? -bounce : bounce; + } + + PhysicsMaterial(); +}; + +#endif // physics_material_override_H diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 6fea2e1a8e..44f9ebaf33 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -65,8 +65,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> for (int i = 0; i < p_points.size(); i++) { - points[i].pos = p_points[i]; - points[i].penalty = 0; + points.write[i].pos = p_points[i]; + points.write[i].penalty = 0; outside_point.x = i == 0 ? p_points[0].x : (MAX(p_points[i].x, outside_point.x)); outside_point.y = i == 0 ? p_points[0].y : (MAX(p_points[i].y, outside_point.y)); @@ -88,8 +88,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> Edge e(p_connections[i], p_connections[i + 1]); ERR_FAIL_INDEX(e.points[0], point_count); ERR_FAIL_INDEX(e.points[1], point_count); - points[p_connections[i]].connections.insert(p_connections[i + 1]); - points[p_connections[i + 1]].connections.insert(p_connections[i]); + points.write[p_connections[i]].connections.insert(p_connections[i + 1]); + points.write[p_connections[i + 1]].connections.insert(p_connections[i]); edges.insert(e); } @@ -126,8 +126,8 @@ void PolygonPathFinder::setup(const Vector<Vector2> &p_points, const Vector<int> } if (valid) { - points[i].connections.insert(j); - points[j].connections.insert(i); + points.write[i].connections.insert(j); + points.write[j].connections.insert(i); } } } @@ -227,21 +227,21 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector int aidx = points.size() - 2; int bidx = points.size() - 1; - points[aidx].pos = from; - points[bidx].pos = to; - points[aidx].distance = 0; - points[bidx].distance = 0; - points[aidx].prev = -1; - points[bidx].prev = -1; - points[aidx].penalty = 0; - points[bidx].penalty = 0; + points.write[aidx].pos = from; + points.write[bidx].pos = to; + points.write[aidx].distance = 0; + points.write[bidx].distance = 0; + points.write[aidx].prev = -1; + points.write[bidx].prev = -1; + points.write[aidx].penalty = 0; + points.write[bidx].penalty = 0; for (int i = 0; i < points.size() - 2; i++) { bool valid_a = true; bool valid_b = true; - points[i].prev = -1; - points[i].distance = 0; + points.write[i].prev = -1; + points.write[i].distance = 0; if (!_is_point_inside(from * 0.5 + points[i].pos * 0.5)) { valid_a = false; @@ -292,26 +292,26 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } if (valid_a) { - points[i].connections.insert(aidx); - points[aidx].connections.insert(i); + points.write[i].connections.insert(aidx); + points.write[aidx].connections.insert(i); } if (valid_b) { - points[i].connections.insert(bidx); - points[bidx].connections.insert(i); + points.write[i].connections.insert(bidx); + points.write[bidx].connections.insert(i); } } //solve graph Set<int> open_list; - points[aidx].distance = 0; - points[aidx].prev = aidx; + points.write[aidx].distance = 0; + points.write[aidx].prev = aidx; for (Set<int>::Element *E = points[aidx].connections.front(); E; E = E->next()) { open_list.insert(E->get()); - points[E->get()].distance = from.distance_to(points[E->get()].pos); - points[E->get()].prev = aidx; + points.write[E->get()].distance = from.distance_to(points[E->get()].pos); + points.write[E->get()].prev = aidx; } bool found_route = false; @@ -342,12 +342,12 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector } } - Point &np = points[least_cost_point]; + const Point &np = points[least_cost_point]; //open the neighbours for search for (Set<int>::Element *E = np.connections.front(); E; E = E->next()) { - Point &p = points[E->get()]; + Point &p = points.write[E->get()]; float distance = np.pos.distance_to(p.pos) + np.distance; if (p.prev != -1) { @@ -392,18 +392,18 @@ Vector<Vector2> PolygonPathFinder::find_path(const Vector2 &p_from, const Vector for (int i = 0; i < points.size() - 2; i++) { - points[i].connections.erase(aidx); - points[i].connections.erase(bidx); - points[i].prev = -1; - points[i].distance = 0; + points.write[i].connections.erase(aidx); + points.write[i].connections.erase(bidx); + points.write[i].prev = -1; + points.write[i].distance = 0; } - points[aidx].connections.clear(); - points[aidx].prev = -1; - points[aidx].distance = 0; - points[bidx].connections.clear(); - points[bidx].prev = -1; - points[bidx].distance = 0; + points.write[aidx].connections.clear(); + points.write[aidx].prev = -1; + points.write[aidx].distance = 0; + points.write[bidx].connections.clear(); + points.write[bidx].prev = -1; + points.write[bidx].distance = 0; return path; } @@ -427,13 +427,13 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) { PoolVector<Vector2>::Read pr = p.read(); for (int i = 0; i < pc; i++) { - points[i].pos = pr[i]; + points.write[i].pos = pr[i]; PoolVector<int> con = c[i]; PoolVector<int>::Read cr = con.read(); int cc = con.size(); for (int j = 0; j < cc; j++) { - points[i].connections.insert(cr[j]); + points.write[i].connections.insert(cr[j]); } } @@ -443,7 +443,7 @@ void PolygonPathFinder::_set_data(const Dictionary &p_data) { if (penalties.size() == pc) { PoolVector<float>::Read pr = penalties.read(); for (int i = 0; i < pc; i++) { - points[i].penalty = pr[i]; + points.write[i].penalty = pr[i]; } } } @@ -566,7 +566,7 @@ Rect2 PolygonPathFinder::get_bounds() const { void PolygonPathFinder::set_point_penalty(int p_point, float p_penalty) { ERR_FAIL_INDEX(p_point, points.size() - 2); - points[p_point].penalty = p_penalty; + points.write[p_point].penalty = p_penalty; } float PolygonPathFinder::get_point_penalty(int p_point) const { diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 597866eb74..fd9989fe72 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -896,7 +896,7 @@ static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32(utf8.length() + 1 | 0x80000000); + f->store_32((utf8.length() + 1) | 0x80000000); } else { f->store_32(utf8.length() + 1); } @@ -1523,7 +1523,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - sorted_er[E->get()] = E->key(); + sorted_er.write[E->get()] = E->key(); } for (int i = 0; i < sorted_er.size(); i++) { diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 36740a307b..f53f03c1c8 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -54,16 +54,20 @@ void Shader::set_code(const String &p_code) { VisualServer::get_singleton()->shader_set_code(shader, p_code); params_cache_dirty = true; - emit_signal(SceneStringNames::get_singleton()->changed); + + emit_changed(); } String Shader::get_code() const { + _update_shader(); return VisualServer::get_singleton()->shader_get_code(shader); } void Shader::get_param_list(List<PropertyInfo> *p_params) const { + _update_shader(); + List<PropertyInfo> local; VisualServer::get_singleton()->shader_get_param_list(shader, &local); params_cache.clear(); @@ -72,6 +76,9 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { for (List<PropertyInfo>::Element *E = local.front(); E; E = E->next()) { PropertyInfo pi = E->get(); + if (default_textures.has(pi.name)) { //do not show default textures + continue; + } pi.name = "shader_param/" + pi.name; params_cache[pi.name] = E->get().name; if (p_params) { @@ -86,6 +93,8 @@ void Shader::get_param_list(List<PropertyInfo> *p_params) const { RID Shader::get_rid() const { + _update_shader(); + return shader; } @@ -98,6 +107,8 @@ void Shader::set_default_texture_param(const StringName &p_param, const Ref<Text default_textures.erase(p_param); VS::get_singleton()->shader_set_default_texture_param(shader, p_param, RID()); } + + emit_changed(); } Ref<Texture> Shader::get_default_texture_param(const StringName &p_param) const { @@ -120,6 +131,9 @@ bool Shader::has_param(const StringName &p_param) const { return params_cache.has(p_param); } +void Shader::_update_shader() const { +} + void Shader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mode"), &Shader::get_mode); @@ -227,5 +241,5 @@ void ResourceFormatSaverShader::get_recognized_extensions(const RES &p_resource, } bool ResourceFormatSaverShader::recognize(const RES &p_resource) const { - return Object::cast_to<Shader>(*p_resource) != NULL; + return p_resource->get_class_name() == "Shader"; //only shader, not inherited } diff --git a/scene/resources/shader.h b/scene/resources/shader.h index 248a6f0125..efc5da7753 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -61,12 +61,13 @@ private: mutable Map<StringName, StringName> params_cache; //map a shader param to a material param.. Map<StringName, Ref<Texture> > default_textures; + virtual void _update_shader() const; //used for visual shader protected: static void _bind_methods(); public: //void set_mode(Mode p_mode); - Mode get_mode() const; + virtual Mode get_mode() const; void set_code(const String &p_code); String get_code() const; diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp index f2d5cb3516..4176aed4d8 100644 --- a/scene/resources/sky_box.cpp +++ b/scene/resources/sky_box.cpp @@ -405,7 +405,7 @@ void ProceduralSky::_update_sky() { } else { Ref<Image> image = _generate_sky(); - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), Image::FORMAT_RGBE9995, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); VS::get_singleton()->texture_set_data(texture, image); _radiance_changed(); } @@ -422,7 +422,7 @@ void ProceduralSky::_queue_update() { void ProceduralSky::_thread_done(const Ref<Image> &p_image) { - VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), Image::FORMAT_RGBE9995, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); + VS::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, Image::FORMAT_RGBE9995, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER | VS::TEXTURE_FLAG_REPEAT); VS::get_singleton()->texture_set_data(texture, p_image); _radiance_changed(); Thread::wait_to_finish(sky_thread); @@ -532,14 +532,14 @@ ProceduralSky::ProceduralSky() { texture = VS::get_singleton()->texture_create(); update_queued = false; - sky_top_color = Color::hex(0x0c74f9ff); - sky_horizon_color = Color::hex(0x8ed2e8ff); - sky_curve = 0.25; + sky_top_color = Color::hex(0xa5d6f1ff); + sky_horizon_color = Color::hex(0xd6eafaff); + sky_curve = 0.09; sky_energy = 1; - ground_bottom_color = Color::hex(0x1a2530ff); - ground_horizon_color = Color::hex(0x7bc9f3ff); - ground_curve = 0.01; + ground_bottom_color = Color::hex(0x282f36ff); + ground_horizon_color = Color::hex(0x6c655fff); + ground_curve = 0.02; ground_energy = 1; sun_color = Color(1, 1, 1); diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index ebad00b068..fb81375b0a 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -905,12 +905,20 @@ bool StyleBoxLine::is_vertical() const { return vertical; } -void StyleBoxLine::set_grow(float p_grow) { - grow = p_grow; +void StyleBoxLine::set_grow_end(float p_grow_end) { + grow_end = p_grow_end; emit_changed(); } -float StyleBoxLine::get_grow() const { - return grow; +float StyleBoxLine::get_grow_end() const { + return grow_end; +} + +void StyleBoxLine::set_grow_begin(float p_grow_begin) { + grow_begin = p_grow_begin; + emit_changed(); +} +float StyleBoxLine::get_grow_begin() const { + return grow_begin; } void StyleBoxLine::_bind_methods() { @@ -919,13 +927,16 @@ void StyleBoxLine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_color"), &StyleBoxLine::get_color); ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &StyleBoxLine::set_thickness); ClassDB::bind_method(D_METHOD("get_thickness"), &StyleBoxLine::get_thickness); - ClassDB::bind_method(D_METHOD("set_grow", "grow"), &StyleBoxLine::set_grow); - ClassDB::bind_method(D_METHOD("get_grow"), &StyleBoxLine::get_grow); + ClassDB::bind_method(D_METHOD("set_grow_begin", "offset"), &StyleBoxLine::set_grow_begin); + ClassDB::bind_method(D_METHOD("get_grow_begin"), &StyleBoxLine::get_grow_begin); + ClassDB::bind_method(D_METHOD("set_grow_end", "offset"), &StyleBoxLine::set_grow_end); + ClassDB::bind_method(D_METHOD("get_grow_end"), &StyleBoxLine::get_grow_end); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &StyleBoxLine::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &StyleBoxLine::is_vertical); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow", "get_grow"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_begin", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_begin", "get_grow_begin"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "grow_end", PROPERTY_HINT_RANGE, "-300,300,1"), "set_grow_end", "get_grow_end"); ADD_PROPERTY(PropertyInfo(Variant::INT, "thickness", PROPERTY_HINT_RANGE, "0,10"), "set_thickness", "get_thickness"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } @@ -941,12 +952,12 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { Rect2i r = p_rect; if (vertical) { - r.position.y -= grow; - r.size.y += grow * 2; + r.position.y -= grow_begin; + r.size.y += (grow_begin + grow_end); r.size.x = thickness; } else { - r.position.x -= grow; - r.size.x += grow * 2; + r.position.x -= grow_begin; + r.size.x += (grow_begin + grow_end); r.size.y = thickness; } @@ -954,7 +965,8 @@ void StyleBoxLine::draw(RID p_canvas_item, const Rect2 &p_rect) const { } StyleBoxLine::StyleBoxLine() { - grow = 1.0; + grow_begin = 1.0; + grow_end = 1.0; thickness = 1; color = Color(0.0, 0.0, 0.0); vertical = false; diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h index c1d84fe19f..ed193a1ab4 100644 --- a/scene/resources/style_box.h +++ b/scene/resources/style_box.h @@ -236,7 +236,8 @@ class StyleBoxLine : public StyleBox { Color color; int thickness; bool vertical; - float grow; + float grow_begin; + float grow_end; protected: virtual float get_style_margin(Margin p_margin) const; @@ -252,8 +253,11 @@ public: void set_vertical(bool p_vertical); bool is_vertical() const; - void set_grow(float p_grow); - float get_grow() const; + void set_grow_begin(float p_grow); + float get_grow_begin() const; + + void set_grow_end(float p_grow); + float get_grow_end() const; virtual Size2 get_center_size() const; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 5a42873d79..ec489e5c5b 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -421,6 +421,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_ Array a = commit_to_arrays(); mesh->add_surface_from_arrays(primitive, a, Array(), p_flags); + if (material.is_valid()) mesh->surface_set_material(surface, material); @@ -465,7 +466,7 @@ void SurfaceTool::deindex() { int idx = 0; for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - varr[idx++] = E->get(); + varr.write[idx++] = E->get(); } vertex_array.clear(); for (List<int>::Element *E = index_array.front(); E; E = E->next()) { @@ -569,19 +570,19 @@ Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_array if (lformat & VS::ARRAY_FORMAT_BONES) { Vector<int> b; b.resize(4); - b[0] = barr[i * 4 + 0]; - b[1] = barr[i * 4 + 1]; - b[2] = barr[i * 4 + 2]; - b[3] = barr[i * 4 + 3]; + b.write[0] = barr[i * 4 + 0]; + b.write[1] = barr[i * 4 + 1]; + b.write[2] = barr[i * 4 + 2]; + b.write[3] = barr[i * 4 + 3]; v.bones = b; } if (lformat & VS::ARRAY_FORMAT_WEIGHTS) { Vector<float> w; w.resize(4); - w[0] = warr[i * 4 + 0]; - w[1] = warr[i * 4 + 1]; - w[2] = warr[i * 4 + 2]; - w[3] = warr[i * 4 + 3]; + w.write[0] = warr[i * 4 + 0]; + w.write[1] = warr[i * 4 + 1]; + w.write[2] = warr[i * 4 + 2]; + w.write[3] = warr[i * 4 + 3]; v.weights = w; } @@ -674,19 +675,19 @@ void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, Li if (lformat & VS::ARRAY_FORMAT_BONES) { Vector<int> b; b.resize(4); - b[0] = barr[i * 4 + 0]; - b[1] = barr[i * 4 + 1]; - b[2] = barr[i * 4 + 2]; - b[3] = barr[i * 4 + 3]; + b.write[0] = barr[i * 4 + 0]; + b.write[1] = barr[i * 4 + 1]; + b.write[2] = barr[i * 4 + 2]; + b.write[3] = barr[i * 4 + 3]; v.bones = b; } if (lformat & VS::ARRAY_FORMAT_WEIGHTS) { Vector<float> w; w.resize(4); - w[0] = warr[i * 4 + 0]; - w[1] = warr[i * 4 + 1]; - w[2] = warr[i * 4 + 2]; - w[3] = warr[i * 4 + 3]; + w.write[0] = warr[i * 4 + 0]; + w.write[1] = warr[i * 4 + 1]; + w.write[2] = warr[i * 4 + 2]; + w.write[3] = warr[i * 4 + 3]; v.weights = w; } @@ -845,7 +846,7 @@ void SurfaceTool::generate_tangents() { vtx.resize(vertex_array.size()); int idx = 0; for (List<Vertex>::Element *E = vertex_array.front(); E; E = E->next()) { - vtx[idx++] = E; + vtx.write[idx++] = E; E->get().binormal = Vector3(); E->get().tangent = Vector3(); } diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp new file mode 100644 index 0000000000..e2fe0adfc5 --- /dev/null +++ b/scene/resources/text_file.cpp @@ -0,0 +1,77 @@ +/*************************************************************************/ +/* text_file.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "text_file.h" + +#include "os/file_access.h" + +bool TextFile::has_text() const { + return text != ""; +} + +String TextFile::get_text() const { + return text; +} + +void TextFile::set_text(const String &p_code) { + text = p_code; +} + +void TextFile::reload_from_file() { + load_text(path); +} + +Error TextFile::load_text(const String &p_path) { + + PoolVector<uint8_t> sourcef; + Error err; + FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + if (err) { + ERR_FAIL_COND_V(err, err); + } + + int len = f->get_len(); + sourcef.resize(len + 1); + PoolVector<uint8_t>::Write w = sourcef.write(); + int r = f->get_buffer(w.ptr(), len); + f->close(); + memdelete(f); + ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w.ptr())) { + ERR_EXPLAIN("Script '" + p_path + "' contains invalid unicode (utf-8), so it was not loaded. Please ensure that scripts are saved in valid utf-8 unicode."); + ERR_FAIL_V(ERR_INVALID_DATA); + } + text = s; + path = p_path; + return OK; +} diff --git a/scene/resources/text_file.h b/scene/resources/text_file.h new file mode 100644 index 0000000000..40b648eebb --- /dev/null +++ b/scene/resources/text_file.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* text_file.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEXTFILE_H +#define TEXTFILE_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" + +class TextFile : public Resource { + + GDCLASS(TextFile, Resource) + +private: + String text; + String path; + +public: + virtual bool has_text() const; + virtual String get_text() const; + virtual void set_text(const String &p_code); + virtual void reload_from_file(); + + void set_file_path(const String &p_path) { path = p_path; } + Error load_text(const String &p_path); +}; + +#endif // TEXTFILE_H diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 54f5aea160..536c653a0c 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -124,7 +124,7 @@ bool ImageTexture::_set(const StringName &p_name, const Variant &p_value) { Size2 s = p_value; w = s.width; h = s.height; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } else if (p_name == "_data") { _set_data(p_value); } else @@ -151,13 +151,6 @@ bool ImageTexture::_get(const StringName &p_name, Variant &r_ret) const { void ImageTexture::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat")); p_list->push_back(PropertyInfo(Variant::OBJECT, "image", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "")); @@ -183,7 +176,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) { void ImageTexture::create(int p_width, int p_height, Image::Format p_format, uint32_t p_flags) { flags = p_flags; - VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, p_format, p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_width, p_height, 0, p_format, VS::TEXTURE_TYPE_2D, p_flags); format = p_format; w = p_width; h = p_height; @@ -196,7 +189,7 @@ void ImageTexture::create_from_image(const Ref<Image> &p_image, uint32_t p_flags h = p_image->get_height(); format = p_image->get_format(); - VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), p_image->get_format(), p_flags); + VisualServer::get_singleton()->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_flags); VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); } @@ -235,6 +228,8 @@ Error ImageTexture::load(const String &p_path) { void ImageTexture::set_data(const Ref<Image> &p_image) { + ERR_FAIL_COND(p_image.is_null()); + VisualServer::get_singleton()->texture_set_data(texture, p_image); _change_notify(); @@ -299,7 +294,7 @@ void ImageTexture::set_size_override(const Size2 &p_size) { w = s.x; if (s.y != 0) h = s.y; - VisualServer::get_singleton()->texture_set_size_override(texture, w, h); + VisualServer::get_singleton()->texture_set_size_override(texture, w, h, 0); } void ImageTexture::set_path(const String &p_path, bool p_take_over) { @@ -590,7 +585,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla int sh = th; int mipmaps = Image::get_image_required_mipmaps(tw, th, format); - int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + int total_size = Image::get_image_data_size(tw, th, format, true); int idx = 0; int ofs = 0; @@ -646,7 +641,7 @@ Error StreamTexture::load(const String &p_path) { if (err) return err; - VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), image->get_format(), lflags); + VS::get_singleton()->texture_allocate(texture, image->get_width(), image->get_height(), 0, image->get_format(), VS::TEXTURE_TYPE_2D, lflags); VS::get_singleton()->texture_set_data(texture, image); w = lw; @@ -1037,7 +1032,7 @@ bool LargeTexture::has_alpha() const { void LargeTexture::set_flags(uint32_t p_flags) { for (int i = 0; i < pieces.size(); i++) { - pieces[i].texture->set_flags(p_flags); + pieces.write[i].texture->set_flags(p_flags); } } @@ -1063,13 +1058,13 @@ int LargeTexture::add_piece(const Point2 &p_offset, const Ref<Texture> &p_textur void LargeTexture::set_piece_offset(int p_idx, const Point2 &p_offset) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].offset = p_offset; + pieces.write[p_idx].offset = p_offset; }; void LargeTexture::set_piece_texture(int p_idx, const Ref<Texture> &p_texture) { ERR_FAIL_INDEX(p_idx, pieces.size()); - pieces[p_idx].texture = p_texture; + pieces.write[p_idx].texture = p_texture; }; void LargeTexture::set_size(const Size2 &p_size) { @@ -1153,7 +1148,6 @@ void LargeTexture::draw_rect(RID p_canvas_item, const Rect2 &p_rect, bool p_tile Size2 scale = p_rect.size / size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1168,7 +1162,6 @@ void LargeTexture::draw_rect_region(RID p_canvas_item, const Rect2 &p_rect, cons Size2 scale = p_rect.size / p_src_rect.size; - RID normal_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID(); for (int i = 0; i < pieces.size(); i++) { // TODO @@ -1193,7 +1186,7 @@ void CubeMap::set_flags(uint32_t p_flags) { flags = p_flags; if (_is_valid()) - VS::get_singleton()->texture_set_flags(cubemap, flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_set_flags(cubemap, flags); } uint32_t CubeMap::get_flags() const { @@ -1209,7 +1202,7 @@ void CubeMap::set_side(Side p_side, const Ref<Image> &p_image) { format = p_image->get_format(); w = p_image->get_width(); h = p_image->get_height(); - VS::get_singleton()->texture_allocate(cubemap, w, h, p_image->get_format(), flags | VS::TEXTURE_FLAG_CUBEMAP); + VS::get_singleton()->texture_allocate(cubemap, w, h, 0, p_image->get_format(), VS::TEXTURE_TYPE_CUBEMAP, flags); } VS::get_singleton()->texture_set_data(cubemap, p_image, VS::CubeMapSide(p_side)); @@ -1320,13 +1313,6 @@ bool CubeMap::_get(const StringName &p_name, Variant &r_ret) const { void CubeMap::_get_property_list(List<PropertyInfo> *p_list) const { - PropertyHint img_hint = PROPERTY_HINT_NONE; - if (storage == STORAGE_COMPRESS_LOSSY) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSY; - } else if (storage == STORAGE_COMPRESS_LOSSLESS) { - img_hint = PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS; - } - p_list->push_back(PropertyInfo(Variant::OBJECT, "side/left", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/right", PROPERTY_HINT_RESOURCE_TYPE, "Image")); p_list->push_back(PropertyInfo(Variant::OBJECT, "side/bottom", PROPERTY_HINT_RESOURCE_TYPE, "Image")); @@ -1472,7 +1458,7 @@ void CurveTexture::_update() { Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data)); - VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(_texture, _width, 1, 0, Image::FORMAT_RF, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(_texture, image); emit_changed(); @@ -1581,7 +1567,7 @@ void GradientTexture::_update() { Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RGBA8, data)); - VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RGBA8, VS::TEXTURE_FLAG_FILTER); + VS::get_singleton()->texture_allocate(texture, width, 1, 0, Image::FORMAT_RGBA8, VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER); VS::get_singleton()->texture_set_data(texture, image); emit_changed(); @@ -1669,3 +1655,532 @@ ProxyTexture::~ProxyTexture() { VS::get_singleton()->free(proxy); } +////////////////////////////////////////////// + +void AnimatedTexture::_update_proxy() { + + _THREAD_SAFE_METHOD_ + + float delta; + if (prev_ticks == 0) { + delta = 0; + prev_ticks = OS::get_singleton()->get_ticks_usec(); + } else { + uint64_t ticks = OS::get_singleton()->get_ticks_usec(); + delta = float(double(ticks - prev_ticks) / 1000000.0); + prev_ticks = ticks; + } + + time += delta; + + float limit; + + if (fps == 0) { + limit = 0; + } else { + limit = 1.0 / fps; + } + + int iter_max = frame_count; + while (iter_max) { + float frame_limit = limit + frames[current_frame].delay_sec; + + if (time > frame_limit) { + current_frame++; + if (current_frame >= frame_count) { + current_frame = 0; + } + time -= frame_limit; + } else { + break; + } + iter_max--; + } + + if (frames[current_frame].texture.is_valid()) { + VisualServer::get_singleton()->texture_set_proxy(proxy, frames[current_frame].texture->get_rid()); + } +} + +void AnimatedTexture::set_frames(int p_frames) { + ERR_FAIL_COND(p_frames < 1 || p_frames > MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frame_count = p_frames; +} +int AnimatedTexture::get_frames() const { + return frame_count; +} + +void AnimatedTexture::set_frame_texture(int p_frame, const Ref<Texture> &p_texture) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frames[p_frame].texture = p_texture; +} +Ref<Texture> AnimatedTexture::get_frame_texture(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, Ref<Texture>()); + + _THREAD_SAFE_METHOD_ + + return frames[p_frame].texture; +} + +void AnimatedTexture::set_frame_delay(int p_frame, float p_delay_sec) { + ERR_FAIL_INDEX(p_frame, MAX_FRAMES); + + _THREAD_SAFE_METHOD_ + + frames[p_frame].delay_sec = p_delay_sec; +} +float AnimatedTexture::get_frame_delay(int p_frame) const { + ERR_FAIL_INDEX_V(p_frame, MAX_FRAMES, 0); + + _THREAD_SAFE_METHOD_ + + return frames[p_frame].delay_sec; +} + +void AnimatedTexture::set_fps(float p_fps) { + ERR_FAIL_COND(p_fps < 0 || p_fps >= 1000); + + fps = p_fps; +} +float AnimatedTexture::get_fps() const { + return fps; +} + +int AnimatedTexture::get_width() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_width(); +} +int AnimatedTexture::get_height() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 1; + } + + return frames[current_frame].texture->get_height(); +} +RID AnimatedTexture::get_rid() const { + return proxy; +} + +bool AnimatedTexture::has_alpha() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return false; + } + + return frames[current_frame].texture->has_alpha(); +} + +Ref<Image> AnimatedTexture::get_data() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return Ref<Image>(); + } + + return frames[current_frame].texture->get_data(); +} + +void AnimatedTexture::set_flags(uint32_t p_flags) { +} +uint32_t AnimatedTexture::get_flags() const { + + _THREAD_SAFE_METHOD_ + + if (!frames[current_frame].texture.is_valid()) { + return 0; + } + + return frames[current_frame].texture->get_flags(); +} + +void AnimatedTexture::_validate_property(PropertyInfo &property) const { + + String prop = property.name; + if (prop.begins_with("frame_")) { + int frame = prop.get_slicec('/', 0).get_slicec('_', 1).to_int(); + if (frame >= frame_count) { + property.usage = 0; + } + } +} + +void AnimatedTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_frames", "frames"), &AnimatedTexture::set_frames); + ClassDB::bind_method(D_METHOD("get_frames"), &AnimatedTexture::get_frames); + + ClassDB::bind_method(D_METHOD("set_fps", "fps"), &AnimatedTexture::set_fps); + ClassDB::bind_method(D_METHOD("get_fps"), &AnimatedTexture::get_fps); + + ClassDB::bind_method(D_METHOD("set_frame_texture", "frame", "texture"), &AnimatedTexture::set_frame_texture); + ClassDB::bind_method(D_METHOD("get_frame_texture", "frame"), &AnimatedTexture::get_frame_texture); + + ClassDB::bind_method(D_METHOD("set_frame_delay", "frame", "delay"), &AnimatedTexture::set_frame_delay); + ClassDB::bind_method(D_METHOD("get_frame_delay", "frame"), &AnimatedTexture::get_frame_delay); + + ClassDB::bind_method(D_METHOD("_update_proxy"), &AnimatedTexture::_update_proxy); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "frames", PROPERTY_HINT_RANGE, "1," + itos(MAX_FRAMES), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_frames", "get_frames"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "fps", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_fps", "get_fps"); + + for (int i = 0; i < MAX_FRAMES; i++) { + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "frame_" + itos(i) + "/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_frame_texture", "get_frame_texture", i); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "frame_" + itos(i) + "/delay_sec", PROPERTY_HINT_RANGE, "0.0,16.0,0.01"), "set_frame_delay", "get_frame_delay", i); + } +} + +AnimatedTexture::AnimatedTexture() { + proxy = VS::get_singleton()->texture_create(); + VisualServer::get_singleton()->texture_set_force_redraw_if_visible(proxy, true); + time = 0; + frame_count = 1; + fps = 4; + prev_ticks = 0; + current_frame = 0; + VisualServer::get_singleton()->connect("frame_pre_draw", this, "_update_proxy"); +} + +AnimatedTexture::~AnimatedTexture() { + VS::get_singleton()->free(proxy); +} +/////////////////////////////// + +void TextureLayered::set_flags(uint32_t p_flags) { + flags = p_flags; + + if (texture.is_valid()) { + VS::get_singleton()->texture_set_flags(texture, flags); + } +} + +uint32_t TextureLayered::get_flags() const { + return flags; +} + +Image::Format TextureLayered::get_format() const { + return format; +} + +uint32_t TextureLayered::get_width() const { + return width; +} + +uint32_t TextureLayered::get_height() const { + return height; +} + +uint32_t TextureLayered::get_depth() const { + return depth; +} + +void TextureLayered::_set_data(const Dictionary &p_data) { + ERR_FAIL_COND(!p_data.has("width")); + ERR_FAIL_COND(!p_data.has("height")); + ERR_FAIL_COND(!p_data.has("depth")); + ERR_FAIL_COND(!p_data.has("format")); + ERR_FAIL_COND(!p_data.has("flags")); + ERR_FAIL_COND(!p_data.has("layers")); + int w = p_data["width"]; + int h = p_data["height"]; + int d = p_data["depth"]; + Image::Format format = Image::Format(int(p_data["format"])); + int flags = p_data["flags"]; + Array layers = p_data["layers"]; + ERR_FAIL_COND(layers.size() != d); + + create(w, h, d, format, flags); + + for (int i = 0; i < layers.size(); i++) { + Ref<Image> img = layers[i]; + ERR_CONTINUE(!img.is_valid()); + ERR_CONTINUE(img->get_format() != format); + ERR_CONTINUE(img->get_width() != w); + ERR_CONTINUE(img->get_height() != h); + set_layer_data(img, i); + } +} + +Dictionary TextureLayered::_get_data() const { + Dictionary d; + d["width"] = width; + d["height"] = height; + d["depth"] = depth; + d["flags"] = flags; + d["format"] = format; + + Array layers; + for (int i = 0; i < depth; i++) { + layers.push_back(get_layer_data(i)); + } + d["layers"] = layers; + return d; +} + +void TextureLayered::create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags) { + VS::get_singleton()->texture_allocate(texture, p_width, p_height, p_depth, p_format, is_3d ? VS::TEXTURE_TYPE_3D : VS::TEXTURE_TYPE_2D_ARRAY, p_flags); + + width = p_width; + height = p_height; + depth = p_depth; + + flags = p_flags; +} + +void TextureLayered::set_layer_data(const Ref<Image> &p_image, int p_layer) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data(texture, p_image, p_layer); +} + +Ref<Image> TextureLayered::get_layer_data(int p_layer) const { + + ERR_FAIL_COND_V(!texture.is_valid(), Ref<Image>()); + return VS::get_singleton()->texture_get_data(texture, p_layer); +} + +void TextureLayered::set_data_partial(const Ref<Image> &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap) { + ERR_FAIL_COND(!texture.is_valid()); + VS::get_singleton()->texture_set_data_partial(texture, p_image, 0, 0, p_image->get_width(), p_image->get_height(), p_x_ofs, p_y_ofs, p_mipmap, p_z); +} + +RID TextureLayered::get_rid() const { + return texture; +} + +void TextureLayered::set_path(const String &p_path, bool p_take_over) { + if (texture.is_valid()) { + VS::get_singleton()->texture_set_path(texture, p_path); + } + + Resource::set_path(p_path, p_take_over); +} + +void TextureLayered::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextureLayered::set_flags); + ClassDB::bind_method(D_METHOD("get_flags"), &TextureLayered::get_flags); + + ClassDB::bind_method(D_METHOD("get_format"), &TextureLayered::get_format); + + ClassDB::bind_method(D_METHOD("get_width"), &TextureLayered::get_width); + ClassDB::bind_method(D_METHOD("get_height"), &TextureLayered::get_height); + ClassDB::bind_method(D_METHOD("get_depth"), &TextureLayered::get_depth); + + ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &TextureLayered::create, DEFVAL(FLAGS_DEFAULT)); + ClassDB::bind_method(D_METHOD("set_layer_data", "image", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::set_layer_data); + ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &TextureLayered::set_data_partial, DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("_set_data", "data"), &TextureLayered::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &TextureLayered::_get_data); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Mipmaps,Repeat,Filter"), "set_flags", "get_flags"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data"); + + BIND_ENUM_CONSTANT(FLAG_MIPMAPS); + BIND_ENUM_CONSTANT(FLAG_REPEAT); + BIND_ENUM_CONSTANT(FLAG_FILTER); + BIND_ENUM_CONSTANT(FLAGS_DEFAULT); +} + +TextureLayered::TextureLayered(bool p_3d) { + is_3d = p_3d; + format = Image::FORMAT_MAX; + flags = FLAGS_DEFAULT; + + width = 0; + height = 0; + depth = 0; + + texture = VS::get_singleton()->texture_create(); +} + +TextureLayered::~TextureLayered() { + if (texture.is_valid()) { + VS::get_singleton()->free(texture); + } +} + +RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String &p_original_path, Error *r_error) { + + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + + Ref<TextureLayered> lt; + Ref<Texture3D> tex3d; + Ref<TextureArray> texarr; + + if (p_path.ends_with("tex3d")) { + tex3d.instance(); + lt = tex3d; + } else if (p_path.ends_with("texarr")) { + texarr.instance(); + lt = texarr; + } else { + ERR_EXPLAIN("Unrecognized layered texture extension"); + ERR_FAIL_V(RES()); + } + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(!f, RES()); + + uint8_t header[5] = { 0, 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + if (header[0] == 'G' && header[1] == 'D' && header[2] == '3' && header[3] == 'T') { + if (tex3d.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(tex3d.is_null(), RES()) + } + } else if (header[0] == 'G' && header[1] == 'D' && header[2] == 'A' && header[3] == 'T') { + if (texarr.is_null()) { + memdelete(f); + ERR_FAIL_COND_V(texarr.is_null(), RES()) + } + } else { + + ERR_EXPLAIN("Unrecognized layered texture file format: " + String((const char *)header)); + ERR_FAIL_V(RES()); + } + + int tw = f->get_32(); + int th = f->get_32(); + int td = f->get_32(); + int flags = f->get_32(); //texture flags! + Image::Format format = Image::Format(f->get_32()); + uint32_t compression = f->get_32(); // 0 - lossless (PNG), 1 - vram, 2 - uncompressed + + lt->create(tw, th, td, format, flags); + + for (int layer = 0; layer < td; layer++) { + + Ref<Image> image; + image.instance(); + + if (compression == COMPRESSION_LOSSLESS) { + //look for a PNG file inside + + int mipmaps = f->get_32(); + Vector<Ref<Image> > mipmap_images; + + for (int i = 0; i < mipmaps; i++) { + uint32_t size = f->get_32(); + + PoolVector<uint8_t> pv; + pv.resize(size); + { + PoolVector<uint8_t>::Write w = pv.write(); + f->get_buffer(w.ptr(), size); + } + + Ref<Image> img = Image::lossless_unpacker(pv); + + if (img.is_null() || img->empty() || format != img->get_format()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + + mipmap_images.push_back(img); + } + + if (mipmap_images.size() == 1) { + + image = mipmap_images[0]; + + } else { + int total_size = Image::get_image_data_size(tw, th, format, true); + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + + int ofs = 0; + for (int i = 0; i < mipmap_images.size(); i++) { + + PoolVector<uint8_t> id = mipmap_images[i]->get_data(); + int len = id.size(); + PoolVector<uint8_t>::Read r = id.read(); + copymem(&w[ofs], r.ptr(), len); + ofs += len; + } + } + + image->create(tw, th, true, format, img_data); + if (image->empty()) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + memdelete(f); + ERR_FAIL_V(RES()); + } + } + + } else { + + //look for regular format + bool mipmaps = (flags & Texture::FLAG_MIPMAPS); + int total_size = Image::get_image_data_size(tw, th, format, mipmaps); + + PoolVector<uint8_t> img_data; + img_data.resize(total_size); + + { + PoolVector<uint8_t>::Write w = img_data.write(); + int bytes = f->get_buffer(w.ptr(), total_size); + if (bytes != total_size) { + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + memdelete(f); + } + ERR_FAIL_V(RES()); + } + } + + image->create(tw, th, mipmaps, format, img_data); + } + + lt->set_layer_data(image, layer); + } + + if (r_error) + *r_error = OK; + + return lt; +} + +void ResourceFormatLoaderTextureLayered::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("tex3d"); + p_extensions->push_back("texarr"); +} +bool ResourceFormatLoaderTextureLayered::handles_type(const String &p_type) const { + return p_type == "Texture3D" || p_type == "TextureArray"; +} +String ResourceFormatLoaderTextureLayered::get_resource_type(const String &p_path) const { + + if (p_path.get_extension().to_lower() == "tex3d") + return "Texture3D"; + if (p_path.get_extension().to_lower() == "texarr") + return "TextureArray"; + return ""; +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index d81fd3b19b..1c18189b2c 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -34,10 +34,11 @@ #include "curve.h" #include "io/resource_loader.h" #include "math_2d.h" +#include "os/mutex.h" +#include "os/thread_safe.h" #include "resource.h" #include "scene/resources/color_ramp.h" #include "servers/visual_server.h" - /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -400,6 +401,88 @@ VARIANT_ENUM_CAST(CubeMap::Flags) VARIANT_ENUM_CAST(CubeMap::Side) VARIANT_ENUM_CAST(CubeMap::Storage) +class TextureLayered : public Resource { + + GDCLASS(TextureLayered, Resource) + +public: + enum Flags { + FLAG_MIPMAPS = VisualServer::TEXTURE_FLAG_MIPMAPS, + FLAG_REPEAT = VisualServer::TEXTURE_FLAG_REPEAT, + FLAG_FILTER = VisualServer::TEXTURE_FLAG_FILTER, + FLAG_CONVERT_TO_LINEAR = VisualServer::TEXTURE_FLAG_CONVERT_TO_LINEAR, + FLAGS_DEFAULT = FLAG_FILTER, + }; + +private: + bool is_3d; + RID texture; + Image::Format format; + uint32_t flags; + + int width; + int height; + int depth; + + void _set_data(const Dictionary &p_data); + Dictionary _get_data() const; + +protected: + static void _bind_methods(); + +public: + void set_flags(uint32_t p_flags); + uint32_t get_flags() const; + + Image::Format get_format() const; + uint32_t get_width() const; + uint32_t get_height() const; + uint32_t get_depth() const; + + void create(uint32_t p_width, uint32_t p_height, uint32_t p_depth, Image::Format p_format, uint32_t p_flags = FLAGS_DEFAULT); + void set_layer_data(const Ref<Image> &p_image, int p_layer); + Ref<Image> get_layer_data(int p_layer) const; + void set_data_partial(const Ref<Image> &p_image, int p_x_ofs, int p_y_ofs, int p_z, int p_mipmap = 0); + + virtual RID get_rid() const; + virtual void set_path(const String &p_path, bool p_take_over = false); + + TextureLayered(bool p_3d = false); + ~TextureLayered(); +}; + +VARIANT_ENUM_CAST(TextureLayered::Flags) + +class Texture3D : public TextureLayered { + + GDCLASS(Texture3D, TextureLayered) +public: + Texture3D() : + TextureLayered(true) {} +}; + +class TextureArray : public TextureLayered { + + GDCLASS(TextureArray, TextureLayered) +public: + TextureArray() : + TextureLayered(false) {} +}; + +class ResourceFormatLoaderTextureLayered : public ResourceFormatLoader { +public: + enum Compression { + COMPRESSION_LOSSLESS, + COMPRESSION_VRAM, + COMPRESSION_UNCOMPRESSED + }; + + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + class CurveTexture : public Texture { GDCLASS(CurveTexture, Texture) @@ -521,4 +604,70 @@ public: ~ProxyTexture(); }; +class AnimatedTexture : public Texture { + GDCLASS(AnimatedTexture, Texture) + + _THREAD_SAFE_CLASS_ + +private: + enum { + MAX_FRAMES = 256 + }; + + RID proxy; + + struct Frame { + + Ref<Texture> texture; + float delay_sec; + + Frame() { + delay_sec = 0; + } + }; + + Frame frames[MAX_FRAMES]; + int frame_count; + int current_frame; + + float fps; + + float time; + + uint64_t prev_ticks; + + void _update_proxy(); + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_frames(int p_frames); + int get_frames() const; + + void set_frame_texture(int p_frame, const Ref<Texture> &p_texture); + Ref<Texture> get_frame_texture(int p_frame) const; + + void set_frame_delay(int p_frame, float p_delay_sec); + float get_frame_delay(int p_frame) const; + + void set_fps(float p_fps); + float get_fps() const; + + virtual int get_width() const; + virtual int get_height() const; + virtual RID get_rid() const; + + virtual bool has_alpha() const; + + virtual void set_flags(uint32_t p_flags); + virtual uint32_t get_flags() const; + + virtual Ref<Image> get_data() const; + + AnimatedTexture(); + ~AnimatedTexture(); +}; + #endif diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 58057cda0c..3d2b6c36de 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -262,7 +262,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial")); p_list->push_back(PropertyInfo(Variant::COLOR, pre + "modulate")); p_list->push_back(PropertyInfo(Variant::RECT2, pre + "region")); - p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE")); + p_list->push_back(PropertyInfo(Variant::INT, pre + "tile_mode", PROPERTY_HINT_ENUM, "SINGLE_TILE,AUTO_TILE,ATLAS_TILE")); if (tile_get_tile_mode(id) == AUTO_TILE) { p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/bitmask_mode", PROPERTY_HINT_ENUM, "2X2,3X3 (minimal),3X3", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/bitmask_flags", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); @@ -272,6 +272,12 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/priority_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + } else if (tile_get_tile_mode(id) == ATLAS_TILE) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/icon_coordinate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "autotile/tile_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "autotile/spacing", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/occluder_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); + p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "autotile/navpoly_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, pre + "occluder_offset")); p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D")); @@ -494,8 +500,21 @@ uint16_t TileSet::autotile_get_bitmask(int p_id, Vector2 p_coord) { const Map<Vector2, uint16_t> &TileSet::autotile_get_bitmask_map(int p_id) { static Map<Vector2, uint16_t> dummy; + static Map<Vector2, uint16_t> dummy_atlas; ERR_FAIL_COND_V(!tile_map.has(p_id), dummy); - return tile_map[p_id].autotile_data.flags; + if (tile_get_tile_mode(p_id) == ATLAS_TILE) { + dummy_atlas = Map<Vector2, uint16_t>(); + Rect2 region = tile_get_region(p_id); + Size2 size = autotile_get_size(p_id); + float spacing = autotile_get_spacing(p_id); + for (int x = 0; x < (region.size.x / (size.x + spacing)); x++) { + for (int y = 0; y < (region.size.y / (size.y + spacing)); y++) { + dummy_atlas.insert(Vector2(x, y), 0); + } + } + return dummy_atlas; + } else + return tile_map[p_id].autotile_data.flags; } Vector2 TileSet::autotile_get_subtile_for_bitmask(int p_id, uint16_t p_bitmask, const Node *p_tilemap_node, const Vector2 &p_tile_location) { @@ -576,7 +595,7 @@ void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_sha ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].shape = p_shape; + tile_map[p_id].shapes_data.write[p_shape_id].shape = p_shape; emit_changed(); } @@ -594,7 +613,7 @@ void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].shape_transform = p_offset; + tile_map[p_id].shapes_data.write[p_shape_id].shape_transform = p_offset; emit_changed(); } @@ -622,7 +641,7 @@ void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_ ERR_FAIL_COND(!tile_map.has(p_id)); if (tile_map[p_id].shapes_data.size() <= p_shape_id) tile_map[p_id].shapes_data.resize(p_shape_id + 1); - tile_map[p_id].shapes_data[p_shape_id].one_way_collision = p_one_way; + tile_map[p_id].shapes_data.write[p_shape_id].one_way_collision = p_one_way; emit_changed(); } @@ -923,6 +942,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_normal_map", "id"), &TileSet::tile_get_normal_map); ClassDB::bind_method(D_METHOD("tile_set_material", "id", "material"), &TileSet::tile_set_material); ClassDB::bind_method(D_METHOD("tile_get_material", "id"), &TileSet::tile_get_material); + ClassDB::bind_method(D_METHOD("tile_set_modulate", "id", "color"), &TileSet::tile_set_modulate); + ClassDB::bind_method(D_METHOD("tile_get_modulate", "id"), &TileSet::tile_get_modulate); ClassDB::bind_method(D_METHOD("tile_set_texture_offset", "id", "texture_offset"), &TileSet::tile_set_texture_offset); ClassDB::bind_method(D_METHOD("tile_get_texture_offset", "id"), &TileSet::tile_get_texture_offset); ClassDB::bind_method(D_METHOD("tile_set_region", "id", "region"), &TileSet::tile_set_region); @@ -974,7 +995,7 @@ void TileSet::_bind_methods() { BIND_ENUM_CONSTANT(SINGLE_TILE); BIND_ENUM_CONSTANT(AUTO_TILE); - BIND_ENUM_CONSTANT(ANIMATED_TILE); + BIND_ENUM_CONSTANT(ATLAS_TILE); } TileSet::TileSet() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index ec635ee5cc..40eee2700d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -75,7 +75,7 @@ public: enum TileMode { SINGLE_TILE, AUTO_TILE, - ANIMATED_TILE + ATLAS_TILE }; struct AutotileData { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp new file mode 100644 index 0000000000..d8fc3677fb --- /dev/null +++ b/scene/resources/visual_shader.cpp @@ -0,0 +1,1555 @@ +#include "visual_shader.h" +#include "servers/visual/shader_types.h" +#include "vmap.h" + +void VisualShaderNode::set_output_port_for_preview(int p_index) { + + port_preview = p_index; +} + +int VisualShaderNode::get_output_port_for_preview() const { + + return port_preview; +} + +void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p_value) { + default_input_values[p_port] = p_value; + emit_changed(); +} + +Variant VisualShaderNode::get_input_port_default_value(int p_port) const { + if (default_input_values.has(p_port)) { + return default_input_values[p_port]; + } + + return Variant(); +} + +bool VisualShaderNode::is_port_separator(int p_index) const { + return false; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + return Vector<VisualShader::DefaultTextureParam>(); +} +String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return String(); +} + +Vector<StringName> VisualShaderNode::get_editable_properties() const { + return Vector<StringName>(); +} + +Array VisualShaderNode::_get_default_input_values() const { + + Array ret; + for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) { + ret.push_back(E->key()); + ret.push_back(E->get()); + } + return ret; +} +void VisualShaderNode::_set_default_input_values(const Array &p_values) { + + if (p_values.size() % 2 == 0) { + for (int i = 0; i < p_values.size(); i += 2) { + default_input_values[p_values[i + 0]] = p_values[i + 1]; + } + } + + emit_changed(); +} + +String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + return String(); +} + +void VisualShaderNode::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview); + ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview); + + ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value); + ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value); + + ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualShaderNode::_set_default_input_values); + ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualShaderNode::_get_default_input_values); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview"); + ADD_PROPERTYNZ(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_default_input_values", "_get_default_input_values"); + ADD_SIGNAL(MethodInfo("editor_refresh_request")); +} + +VisualShaderNode::VisualShaderNode() { + port_preview = -1; +} + +///////////////////////////////////////////////////////// + +void VisualShader::add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id) { + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(p_id < 2); + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(g->nodes.has(p_id)); + Node n; + n.node = p_node; + n.position = p_position; + + Ref<VisualShaderNodeUniform> uniform = n.node; + if (uniform.is_valid()) { + String valid_name = validate_uniform_name(uniform->get_uniform_name(), uniform); + uniform->set_uniform_name(valid_name); + } + + Ref<VisualShaderNodeInput> input = n.node; + if (input.is_valid()) { + input->shader_mode = shader_mode; + input->shader_type = p_type; + input->connect("input_type_changed", this, "_input_type_changed", varray(p_type, p_id)); + } + + n.node->connect("changed", this, "_queue_update"); + + g->nodes[p_id] = n; + + _queue_update(); +} + +void VisualShader::set_node_position(Type p_type, int p_id, const Vector2 &p_position) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(!g->nodes.has(p_id)); + g->nodes[p_id].position = p_position; +} + +Vector2 VisualShader::get_node_position(Type p_type, int p_id) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector2()); + const Graph *g = &graph[p_type]; + ERR_FAIL_COND_V(!g->nodes.has(p_id), Vector2()); + return g->nodes[p_id].position; +} +Ref<VisualShaderNode> VisualShader::get_node(Type p_type, int p_id) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Ref<VisualShaderNode>()); + const Graph *g = &graph[p_type]; + ERR_FAIL_COND_V(!g->nodes.has(p_id), Ref<VisualShaderNode>()); + return g->nodes[p_id].node; +} + +Vector<int> VisualShader::get_node_list(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Vector<int>()); + const Graph *g = &graph[p_type]; + + Vector<int> ret; + for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + + return ret; +} +int VisualShader::get_valid_node_id(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, NODE_ID_INVALID); + const Graph *g = &graph[p_type]; + return g->nodes.size() ? MAX(2, g->nodes.back()->key() + 1) : 2; +} + +int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const { + for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) { + if (E->get().node == p_node) + return E->key(); + } + + return NODE_ID_INVALID; +} + +void VisualShader::remove_node(Type p_type, int p_id) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + ERR_FAIL_COND(p_id < 2); + Graph *g = &graph[p_type]; + ERR_FAIL_COND(!g->nodes.has(p_id)); + + Ref<VisualShaderNodeInput> input = g->nodes[p_id].node; + if (input.is_valid()) { + input->disconnect("input_type_changed", this, "_input_type_changed"); + } + + g->nodes[p_id].node->disconnect("changed", this, "_queue_update"); + + g->nodes.erase(p_id); + + for (List<Connection>::Element *E = g->connections.front(); E;) { + List<Connection>::Element *N = E->next(); + if (E->get().from_node == p_id || E->get().to_node == p_id) { + g->connections.erase(E); + } + E = N; + } + + _queue_update(); +} + +bool VisualShader::is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false); + const Graph *g = &graph[p_type]; + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + return true; + } + } + + return false; +} + +bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const { + + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, false); + const Graph *g = &graph[p_type]; + + if (!g->nodes.has(p_from_node)) + return false; + + if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) + return false; + + if (!g->nodes.has(p_to_node)) + return false; + + if (p_to_port < 0 || p_to_port >= g->nodes[p_to_node].node->get_input_port_count()) + return false; + + VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); + VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); + + if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { + return false; + } + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + return false; + } + } + + return true; +} + +Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, ERR_CANT_CONNECT); + Graph *g = &graph[p_type]; + + ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER); + ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER); + + VisualShaderNode::PortType from_port_type = g->nodes[p_from_node].node->get_output_port_type(p_from_port); + VisualShaderNode::PortType to_port_type = g->nodes[p_to_node].node->get_input_port_type(p_to_port); + + if (MAX(0, from_port_type - 1) != (MAX(0, to_port_type - 1))) { + ERR_EXPLAIN("Incompatible port types (scalar/vec with transform"); + ERR_FAIL_V(ERR_INVALID_PARAMETER) + return ERR_INVALID_PARAMETER; + } + + for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + ERR_FAIL_V(ERR_ALREADY_EXISTS); + } + } + + Connection c; + c.from_node = p_from_node; + c.from_port = p_from_port; + c.to_node = p_to_node; + c.to_port = p_to_port; + g->connections.push_back(c); + + _queue_update(); + return OK; +} +void VisualShader::disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + Graph *g = &graph[p_type]; + + for (List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + + if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { + g->connections.erase(E); + _queue_update(); + return; + } + } +} + +Array VisualShader::_get_node_connections(Type p_type) const { + ERR_FAIL_INDEX_V(p_type, TYPE_MAX, Array()); + const Graph *g = &graph[p_type]; + + Array ret; + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + Dictionary d; + d["from_node"] = E->get().from_node; + d["from_port"] = E->get().from_port; + d["to_node"] = E->get().to_node; + d["to_port"] = E->get().to_port; + ret.push_back(d); + } + + return ret; +} +void VisualShader::get_node_connections(Type p_type, List<Connection> *r_connections) const { + ERR_FAIL_INDEX(p_type, TYPE_MAX); + const Graph *g = &graph[p_type]; + + for (const List<Connection>::Element *E = g->connections.front(); E; E = E->next()) { + r_connections->push_back(E->get()); + } +} + +void VisualShader::set_mode(Mode p_mode) { + if (shader_mode == p_mode) { + return; + } + + //erase input/output connections + modes.clear(); + flags.clear(); + shader_mode = p_mode; + for (int i = 0; i < TYPE_MAX; i++) { + + for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + + Ref<VisualShaderNodeInput> input = E->get().node; + if (input.is_valid()) { + input->shader_mode = shader_mode; + //input->input_index = 0; + } + } + + Ref<VisualShaderNodeOutput> output = graph[i].nodes[NODE_ID_OUTPUT].node; + output->shader_mode = shader_mode; + + // clear connections since they are no longer valid + for (List<Connection>::Element *E = graph[i].connections.front(); E;) { + + bool keep = true; + + List<Connection>::Element *N = E->next(); + + int from = E->get().from_node; + int to = E->get().to_node; + + if (!graph[i].nodes.has(from)) { + keep = false; + } else { + Ref<VisualShaderNode> from_node = graph[i].nodes[from].node; + if (from_node->is_class("VisualShaderNodeOutput") || from_node->is_class("VisualShaderNodeInput")) { + keep = false; + } + } + + if (!graph[i].nodes.has(to)) { + keep = false; + } else { + Ref<VisualShaderNode> to_node = graph[i].nodes[to].node; + if (to_node->is_class("VisualShaderNodeOutput") || to_node->is_class("VisualShaderNodeInput")) { + keep = false; + } + } + + if (!keep) { + graph[i].connections.erase(E); + } + E = N; + } + } + + _queue_update(); + _change_notify(); +} + +void VisualShader::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 VisualShader::get_graph_offset() const { + return graph_offset; +} + +Shader::Mode VisualShader::get_mode() const { + return shader_mode; +} + +String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const { + + Ref<VisualShaderNode> node = get_node(p_type, p_node); + ERR_FAIL_COND_V(!node.is_valid(), String()); + ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String()); + ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String()); + + StringBuilder global_code; + StringBuilder code; + + global_code += String() + "shader_type canvas_item;\n"; + + //make it faster to go around through shader + VMap<ConnectionKey, const List<Connection>::Element *> input_connections; + VMap<ConnectionKey, const List<Connection>::Element *> output_connections; + + for (const List<Connection>::Element *E = graph[p_type].connections.front(); E; E = E->next()) { + ConnectionKey from_key; + from_key.node = E->get().from_node; + from_key.port = E->get().from_port; + + output_connections.insert(from_key, E); + + ConnectionKey to_key; + to_key.node = E->get().to_node; + to_key.port = E->get().to_port; + + input_connections.insert(to_key, E); + } + + code += "\nvoid fragment() {\n"; + + Set<int> processed; + Error err = _write_node(p_type, global_code, code, default_tex_params, input_connections, output_connections, p_node, processed, true); + ERR_FAIL_COND_V(err != OK, String()); + + if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_SCALAR) { + code += "\tCOLOR.rgb = vec3( n_out" + itos(p_node) + "p" + itos(p_port) + " );\n"; + } else { + code += "\tCOLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n"; + } + code += "}\n"; + + //set code secretly + global_code += "\n\n"; + String final_code = global_code; + final_code += code; + //print_line(final_code); + return final_code; +} + +#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z')) + +#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_') + +String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const { + + String name = p_name; //validate name first + while (name.length() && !IS_INITIAL_CHAR(name[0])) { + name = name.substr(1, name.length() - 1); + } + if (name != String()) { + + String valid_name; + + for (int i = 0; i < name.length(); i++) { + if (IS_SYMBOL_CHAR(name[i])) { + valid_name += String::chr(name[i]); + } else if (name[i] == ' ') { + valid_name += "_"; + } + } + + name = valid_name; + } + + if (name == String()) { + name = p_uniform->get_caption(); + } + + int attempt = 1; + + while (true) { + + bool exists = false; + for (int i = 0; i < TYPE_MAX; i++) { + for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + Ref<VisualShaderNodeUniform> node = E->get().node; + if (node == p_uniform) { //do not test on self + continue; + } + if (node.is_valid() && node->get_uniform_name() == name) { + exists = true; + break; + } + } + if (exists) { + break; + } + } + + if (exists) { + //remove numbers, put new and try again + attempt++; + while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') { + name = name.substr(0, name.length() - 1); + } + ERR_FAIL_COND_V(name == String(), String()); + name += itos(attempt); + } else { + break; + } + } + + return name; +} + +VisualShader::RenderModeEnums VisualShader::render_mode_enums[] = { + { Shader::MODE_SPATIAL, "blend" }, + { Shader::MODE_SPATIAL, "depth_draw" }, + { Shader::MODE_SPATIAL, "cull" }, + { Shader::MODE_SPATIAL, "diffuse" }, + { Shader::MODE_SPATIAL, "specular" }, + { Shader::MODE_CANVAS_ITEM, "blend" }, + { Shader::MODE_CANVAS_ITEM, NULL } +}; + +static const char *type_string[VisualShader::TYPE_MAX] = { + "vertex", + "fragment", + "light" +}; +bool VisualShader::_set(const StringName &p_name, const Variant &p_value) { + + String name = p_name; + if (name == "mode") { + set_mode(Shader::Mode(int(p_value))); + return true; + } else if (name.begins_with("flags/")) { + StringName flag = name.get_slicec('/', 1); + bool enable = p_value; + if (enable) { + flags.insert(flag); + } else { + flags.erase(flag); + } + _queue_update(); + return true; + } else if (name.begins_with("modes/")) { + String mode = name.get_slicec('/', 1); + int value = p_value; + if (value == 0) { + modes.erase(mode); //means its default anyway, so dont store it + } else { + modes[mode] = value; + } + _queue_update(); + return true; + } else if (name.begins_with("nodes/")) { + String typestr = name.get_slicec('/', 1); + Type type = TYPE_VERTEX; + for (int i = 0; i < TYPE_MAX; i++) { + if (typestr == type_string[i]) { + type = Type(i); + break; + } + } + + String index = name.get_slicec('/', 2); + if (index == "connections") { + + Vector<int> conns = p_value; + if (conns.size() % 4 == 0) { + for (int i = 0; i < conns.size(); i += 4) { + connect_nodes(type, conns[i + 0], conns[i + 1], conns[i + 2], conns[i + 3]); + } + } + return true; + } + + int id = index.to_int(); + String what = name.get_slicec('/', 3); + + if (what == "node") { + add_node(type, p_value, Vector2(), id); + return true; + } else if (what == "position") { + set_node_position(type, id, p_value); + return true; + } + } + return false; +} + +bool VisualShader::_get(const StringName &p_name, Variant &r_ret) const { + + String name = p_name; + if (name == "mode") { + r_ret = get_mode(); + return true; + } else if (name.begins_with("flags/")) { + StringName flag = name.get_slicec('/', 1); + r_ret = flags.has(flag); + return true; + } else if (name.begins_with("modes/")) { + String mode = name.get_slicec('/', 1); + if (modes.has(mode)) { + r_ret = modes[mode]; + } else { + r_ret = 0; + } + return true; + } else if (name.begins_with("nodes/")) { + String typestr = name.get_slicec('/', 1); + Type type = TYPE_VERTEX; + for (int i = 0; i < TYPE_MAX; i++) { + if (typestr == type_string[i]) { + type = Type(i); + break; + } + } + + String index = name.get_slicec('/', 2); + if (index == "connections") { + + Vector<int> conns; + for (const List<Connection>::Element *E = graph[type].connections.front(); E; E = E->next()) { + conns.push_back(E->get().from_node); + conns.push_back(E->get().from_port); + conns.push_back(E->get().to_node); + conns.push_back(E->get().to_port); + } + + r_ret = conns; + return true; + } + + int id = index.to_int(); + String what = name.get_slicec('/', 3); + + if (what == "node") { + r_ret = get_node(type, id); + return true; + } else if (what == "position") { + r_ret = get_node_position(type, id); + return true; + } + } + return false; +} +void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const { + + //mode + p_list->push_back(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Spatial,CanvasItem,Particles")); + //render modes + + Map<String, String> blend_mode_enums; + Set<String> toggles; + + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + int idx = 0; + bool in_enum = false; + while (render_mode_enums[idx].string) { + if (mode.begins_with(render_mode_enums[idx].string)) { + String begin = render_mode_enums[idx].string; + String option = mode.replace_first(begin + "_", ""); + if (!blend_mode_enums.has(begin)) { + blend_mode_enums[begin] = option; + } else { + blend_mode_enums[begin] += "," + option; + } + in_enum = true; + break; + } + idx++; + } + + if (!in_enum) { + toggles.insert(mode); + } + } + + for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) { + + p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get())); + } + + for (Set<String>::Element *E = toggles.front(); E; E = E->next()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "flags/" + E->get())); + } + + for (int i = 0; i < TYPE_MAX; i++) { + for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) { + + String prop_name = "nodes/"; + prop_name += type_string[i]; + prop_name += "/" + itos(E->key()); + + if (E->key() != NODE_ID_OUTPUT) { + + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } + p_list->push_back(PropertyInfo(Variant::POOL_INT_ARRAY, "nodes/" + String(type_string[i]) + "/connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + } +} + +Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const { + + const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node; + + //check inputs recursively first + int input_count = vsnode->get_input_port_count(); + for (int i = 0; i < input_count; i++) { + ConnectionKey ck; + ck.node = node; + ck.port = i; + + if (input_connections.has(ck)) { + int from_node = input_connections[ck]->get().from_node; + if (processed.has(from_node)) { + continue; + } + + Error err = _write_node(type, global_code, code, def_tex_params, input_connections, output_connections, from_node, processed, for_preview); + if (err) + return err; + } + } + + // then this node + + code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n"; + Vector<String> input_vars; + + input_vars.resize(vsnode->get_input_port_count()); + String *inputs = input_vars.ptrw(); + + for (int i = 0; i < input_count; i++) { + ConnectionKey ck; + ck.node = node; + ck.port = i; + + if (input_connections.has(ck)) { + //connected to something, use that output + int from_node = input_connections[ck]->get().from_node; + int from_port = input_connections[ck]->get().from_port; + + VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i); + VisualShaderNode::PortType out_type = graph[type].nodes[from_node].node->get_output_port_type(from_port); + + String src_var = "n_out" + itos(from_node) + "p" + itos(from_port); + + if (in_type == out_type) { + inputs[i] = src_var; + } else if (in_type == VisualShaderNode::PORT_TYPE_SCALAR && out_type == VisualShaderNode::PORT_TYPE_VECTOR) { + inputs[i] = "dot(" + src_var + ",vec3(0.333333,0.333333,0.333333))"; + } else if (in_type == VisualShaderNode::PORT_TYPE_VECTOR && out_type == VisualShaderNode::PORT_TYPE_SCALAR) { + inputs[i] = "vec3(" + src_var + ")"; + } + } else { + + Variant defval = vsnode->get_input_port_default_value(i); + if (defval.get_type() == Variant::REAL || defval.get_type() == Variant::INT) { + float val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + code += "\tfloat " + inputs[i] + " = " + vformat("%.5f", val) + ";\n"; + } else if (defval.get_type() == Variant::VECTOR3) { + Vector3 val = defval; + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + code += "\tvec3 " + inputs[i] + " = " + vformat("vec3(%.5f,%.5f,%.5f);\n", val.x, val.y, val.z); + } else if (defval.get_type() == Variant::TRANSFORM) { + Transform val = defval; + val.basis.transpose(); + inputs[i] = "n_in" + itos(node) + "p" + itos(i); + Array values; + for (int i = 0; i < 3; i++) { + values.push_back(val.basis[i].x); + values.push_back(val.basis[i].y); + values.push_back(val.basis[i].z); + } + values.push_back(val.origin.x); + values.push_back(val.origin.y); + values.push_back(val.origin.z); + bool err = false; + code += "\tmat4 " + inputs[i] + " = " + String("mat4( vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,0.0),vec4(%.5f,%.5f,%.5f,1.0) );\n").sprintf(values, &err); + } else { + //will go empty, node is expected to know what it is doing at this point and handle it + } + } + } + + int output_count = vsnode->get_output_port_count(); + Vector<String> output_vars; + output_vars.resize(vsnode->get_output_port_count()); + String *outputs = output_vars.ptrw(); + + for (int i = 0; i < output_count; i++) { + + outputs[i] = "n_out" + itos(node) + "p" + itos(i); + switch (vsnode->get_output_port_type(i)) { + case VisualShaderNode::PORT_TYPE_SCALAR: code += String() + "\tfloat " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_VECTOR: code += String() + "\tvec3 " + outputs[i] + ";\n"; break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: code += String() + "\tmat4 " + outputs[i] + ";\n"; break; + default: {} + } + } + + Vector<VisualShader::DefaultTextureParam> params = vsnode->get_default_texture_parameters(type, node); + for (int i = 0; i < params.size(); i++) { + def_tex_params.push_back(params[i]); + } + + Ref<VisualShaderNodeInput> input = vsnode; + + if (input.is_valid() && for_preview) { + //handle for preview + code += input->generate_code_for_preview(type, node, inputs, outputs); + } else { + //handle normally + global_code += vsnode->generate_global(get_mode(), type, node); + code += vsnode->generate_code(get_mode(), type, node, inputs, outputs); + } + code += "\n"; // + processed.insert(node); + + return OK; +} + +void VisualShader::_update_shader() const { + if (!dirty) + return; + + dirty = false; + + StringBuilder global_code; + StringBuilder code; + Vector<VisualShader::DefaultTextureParam> default_tex_params; + static const char *shader_mode_str[Shader::MODE_MAX] = { "spatial", "canvas_item", "particles" }; + + global_code += String() + "shader_type " + shader_mode_str[shader_mode] + ";\n"; + + String render_mode; + + { + //fill render mode enums + int idx = 0; + while (render_mode_enums[idx].string) { + + if (shader_mode == render_mode_enums[idx].mode) { + + if (modes.has(render_mode_enums[idx].string)) { + + int which = modes[render_mode_enums[idx].string]; + int count = 0; + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + if (mode.begins_with(render_mode_enums[idx].string)) { + if (count == which) { + if (render_mode != String()) { + render_mode += ", "; + } + render_mode += mode; + break; + } + count++; + } + } + } + } + idx++; + } + + //fill render mode flags + for (int i = 0; i < ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode)).size(); i++) { + + String mode = ShaderTypes::get_singleton()->get_modes(VisualServer::ShaderMode(shader_mode))[i]; + if (flags.has(mode)) { + if (render_mode != String()) { + render_mode += ", "; + } + render_mode += mode; + } + } + } + + if (render_mode != String()) { + + global_code += "render_mode " + render_mode + ";\n\n"; + } + + static const char *func_name[TYPE_MAX] = { "vertex", "fragment", "light" }; + + for (int i = 0; i < TYPE_MAX; i++) { + + //make it faster to go around through shader + VMap<ConnectionKey, const List<Connection>::Element *> input_connections; + VMap<ConnectionKey, const List<Connection>::Element *> output_connections; + + for (const List<Connection>::Element *E = graph[i].connections.front(); E; E = E->next()) { + ConnectionKey from_key; + from_key.node = E->get().from_node; + from_key.port = E->get().from_port; + + output_connections.insert(from_key, E); + + ConnectionKey to_key; + to_key.node = E->get().to_node; + to_key.port = E->get().to_port; + + input_connections.insert(to_key, E); + } + + code += "\nvoid " + String(func_name[i]) + "() {\n"; + + Set<int> processed; + Error err = _write_node(Type(i), global_code, code, default_tex_params, input_connections, output_connections, NODE_ID_OUTPUT, processed, false); + ERR_FAIL_COND(err != OK); + + code += "}\n"; + } + + //set code secretly + global_code += "\n\n"; + String final_code = global_code; + final_code += code; + const_cast<VisualShader *>(this)->set_code(final_code); + //print_line(final_code); + for (int i = 0; i < default_tex_params.size(); i++) { + const_cast<VisualShader *>(this)->set_default_texture_param(default_tex_params[i].name, default_tex_params[i].param); + } +} + +void VisualShader::_queue_update() { + if (dirty) { + return; + } + + dirty = true; + call_deferred("_update_shader"); +} + +void VisualShader::_input_type_changed(Type p_type, int p_id) { + //erase connections using this input, as type changed + Graph *g = &graph[p_type]; + + for (List<Connection>::Element *E = g->connections.front(); E;) { + List<Connection>::Element *N = E->next(); + if (E->get().from_node == p_id) { + g->connections.erase(E); + } + E = N; + } +} + +void VisualShader::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &VisualShader::set_mode); + + ClassDB::bind_method(D_METHOD("add_node", "type", "node", "position", "id"), &VisualShader::add_node); + ClassDB::bind_method(D_METHOD("set_node_position", "type", "id", "position"), &VisualShader::set_node_position); + + ClassDB::bind_method(D_METHOD("get_node", "type", "id"), &VisualShader::get_node); + ClassDB::bind_method(D_METHOD("get_node_position", "type", "id"), &VisualShader::get_node_position); + + ClassDB::bind_method(D_METHOD("get_node_list", "type"), &VisualShader::get_node_list); + ClassDB::bind_method(D_METHOD("get_valid_node_id", "type"), &VisualShader::get_valid_node_id); + + ClassDB::bind_method(D_METHOD("remove_node", "type", "id"), &VisualShader::remove_node); + + ClassDB::bind_method(D_METHOD("is_node_connection", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); + ClassDB::bind_method(D_METHOD("can_connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::is_node_connection); + + ClassDB::bind_method(D_METHOD("connect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::connect_nodes); + ClassDB::bind_method(D_METHOD("disconnect_nodes", "type", "from_node", "from_port", "to_node", "to_port"), &VisualShader::disconnect_nodes); + + ClassDB::bind_method(D_METHOD("get_node_connections", "type"), &VisualShader::_get_node_connections); + + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &VisualShader::set_graph_offset); + ClassDB::bind_method(D_METHOD("get_graph_offset"), &VisualShader::get_graph_offset); + + ClassDB::bind_method(D_METHOD("_queue_update"), &VisualShader::_queue_update); + ClassDB::bind_method(D_METHOD("_update_shader"), &VisualShader::_update_shader); + + ClassDB::bind_method(D_METHOD("_input_type_changed"), &VisualShader::_input_type_changed); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); + + BIND_ENUM_CONSTANT(TYPE_VERTEX); + BIND_ENUM_CONSTANT(TYPE_FRAGMENT); + BIND_ENUM_CONSTANT(TYPE_LIGHT); + BIND_ENUM_CONSTANT(TYPE_MAX); + + BIND_CONSTANT(NODE_ID_INVALID); + BIND_CONSTANT(NODE_ID_OUTPUT); +} + +VisualShader::VisualShader() { + shader_mode = Shader::MODE_SPATIAL; + + for (int i = 0; i < TYPE_MAX; i++) { + Ref<VisualShaderNodeOutput> output; + output.instance(); + output->shader_type = Type(i); + output->shader_mode = shader_mode; + graph[i].nodes[NODE_ID_OUTPUT].node = output; + graph[i].nodes[NODE_ID_OUTPUT].position = Vector2(400, 150); + } + + dirty = true; +} + +/////////////////////////////////////////////////////////// + +const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { + // Spatial, Vertex + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0)" }, + + // Spatial, Fragment + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV2,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "float(FRONT_FACING ? 1.0 : 0.0)" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "attenuation", "ATTENUATION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "projection", "PROJECTION_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "extra", "EXTRA_MATRIX" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "light_pass", "float(AT_LIGHT_PASS ? 1.0 : 0.0)" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vec", "vec3(LIGHT_VEC,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_height", "LIGHT_HEIGHT" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_alpha", "LIGHT_COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_uv", "vec3(LIGHT_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow_color", "SHADOW_COLOR.rgb" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "point_coord", "vec2(POINT_COORD,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "restart", "float(RESTART ? 1.0 : 0.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "active", "float(ACTIVE ? 1.0 : 0.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "lifetime", "LIFETIME" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "index", "float(INDEX)" }, + + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "emission_transform", "EMISSION_TRANSFORM" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = { + + // Spatial, Fragment + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0,1.0,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0,0.0,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0,1.0, 0.0)" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "vp_size", "vec3(1.0, 1.0, 0.0)" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(UV,0.0)" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "vec3(0.0,0.0,1.0)" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +int VisualShaderNodeInput::get_input_port_count() const { + + return 0; +} +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_port_type(int p_port) const { + + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeInput::get_input_port_name(int p_port) const { + + return ""; +} + +int VisualShaderNodeInput::get_output_port_count() const { + + return 1; +} +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_output_port_type(int p_port) const { + + return get_input_type_by_name(input_name); +} +String VisualShaderNodeInput::get_output_port_name(int p_port) const { + return ""; +} + +String VisualShaderNodeInput::get_caption() const { + return TTR("Input"); +} + +String VisualShaderNodeInput::generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + + String code; + + while (preview_ports[idx].mode != Shader::MODE_MAX) { + if (preview_ports[idx].mode == shader_mode && preview_ports[idx].shader_type == shader_type && preview_ports[idx].name == input_name) { + code = "\t" + p_output_vars[0] + " = " + preview_ports[idx].string + ";\n"; + break; + } + idx++; + } + + if (code == String()) { + switch (get_output_port_type(0)) { + case PORT_TYPE_SCALAR: { + code = "\t" + p_output_vars[0] + " = 0.0;\n"; + } break; //default (none found) is scalar + case PORT_TYPE_VECTOR: { + code = "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + } break; //default (none found) is scalar + case PORT_TYPE_TRANSFORM: { + code = "\t" + p_output_vars[0] + " = mat4( vec4(1.0,0.0,0.0,0.0), vec4(0.0,1.0,0.0,0.0), vec4(0.0,0.0,1.0,0.0), vec4(0.0,0.0,0.0,1.0) );\n"; + } break; //default (none found) is scalar + } + } + + return code; +} + +String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + + String code; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == input_name) { + code = "\t" + p_output_vars[0] + " = " + ports[idx].string + ";\n"; + break; + } + idx++; + } + + if (code == String()) { + code = "\t" + p_output_vars[0] + " = 0.0;\n"; //default (none found) is scalar + } + + return code; +} + +void VisualShaderNodeInput::set_input_name(String p_name) { + PortType prev_type = get_input_type_by_name(input_name); + input_name = p_name; + emit_changed(); + if (get_input_type_by_name(input_name) != prev_type) { + emit_signal("input_type_changed"); + } +} + +String VisualShaderNodeInput::get_input_name() const { + return input_name; +} + +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_type_by_name(String p_name) const { + + int idx = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type && ports[idx].name == p_name) { + return ports[idx].type; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +int VisualShaderNodeInput::get_input_index_count() const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + count++; + } + idx++; + } + + return count; +} + +VisualShaderNodeInput::PortType VisualShaderNodeInput::get_input_index_type(int p_index) const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_index) { + return ports[idx].type; + } + count++; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeInput::get_input_index_name(int p_index) const { + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_index) { + return ports[idx].name; + } + count++; + } + idx++; + } + + return ""; +} + +void VisualShaderNodeInput::_validate_property(PropertyInfo &property) const { + + if (property.name == "input_name") { + String port_list; + + int idx = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (port_list != String()) { + port_list += ","; + } + port_list += ports[idx].name; + } + idx++; + } + + if (port_list == "") { + port_list = TTR("None"); + } + property.hint_string = port_list; + } +} + +Vector<StringName> VisualShaderNodeInput::get_editable_properties() const { + Vector<StringName> props; + props.push_back("input_name"); + return props; +} + +void VisualShaderNodeInput::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_input_name", "name"), &VisualShaderNodeInput::set_input_name); + ClassDB::bind_method(D_METHOD("get_input_name"), &VisualShaderNodeInput::get_input_name); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_name", PROPERTY_HINT_ENUM, ""), "set_input_name", "get_input_name"); + ADD_SIGNAL(MethodInfo("input_type_changed")); +} +VisualShaderNodeInput::VisualShaderNodeInput() { + input_name = "[None]"; + // changed when set + shader_type = VisualShader::TYPE_MAX; + shader_mode = Shader::MODE_MAX; +} + +//////////////////////////////////////////// + +const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = { + // Spatial, Vertex + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "UV2:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + // Spatial, Fragment + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim_tint", "RIM_TINT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat", "CLEARCOAT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "clearcoat_gloss", "CLEARCOAT_GLOSS" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "anisotropy_flow", "ANISOTROPY_FLOW:xy" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "transmission", "TRANSMISSION" }, + + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor", "ALPHA_SCISSOR" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" }, + + // Spatial, Light + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" }, + // Canvas Item, Vertex + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX:xy" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "UV:xy" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + // Canvas Item, Fragment + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normalmap", "NORMALMAP" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normalmap_depth", "NORMALMAP_DEPTH" }, + // Canvas Item, Light + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" }, + { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.rgb" }, + // Particles, Vertex + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" }, + { Shader::MODE_PARTICLES, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" }, + { Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, NULL, NULL }, +}; + +int VisualShaderNodeOutput::get_input_port_count() const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + count++; + } + idx++; + } + + return count; +} + +VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_input_port_type(int p_port) const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_port) { + return ports[idx].type; + } + count++; + } + idx++; + } + + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeOutput::get_input_port_name(int p_port) const { + + int idx = 0; + int count = 0; + + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + if (count == p_port) { + return String(ports[idx].name).capitalize(); + } + count++; + } + idx++; + } + + return String(); +} + +Variant VisualShaderNodeOutput::get_input_port_default_value(int p_port) const { + return Variant(); +} + +int VisualShaderNodeOutput::get_output_port_count() const { + + return 0; +} +VisualShaderNodeOutput::PortType VisualShaderNodeOutput::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeOutput::get_output_port_name(int p_port) const { + return String(); +} + +bool VisualShaderNodeOutput::is_port_separator(int p_index) const { + + if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) { + String name = get_input_port_name(p_index); + return (name == "Normal" || name == "Rim" || name == "Alpha Scissor"); + } + return false; +} + +String VisualShaderNodeOutput::get_caption() const { + return TTR("Output"); +} + +String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + int idx = 0; + int count = 0; + + String code; + while (ports[idx].mode != Shader::MODE_MAX) { + if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) { + + if (p_input_vars[count] != String()) { + String s = ports[idx].string; + if (s.find(":") != -1) { + code += "\t" + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n"; + } else { + code += "\t" + s + " = " + p_input_vars[count] + ";\n"; + } + } + count++; + } + idx++; + } + + return code; +} + +VisualShaderNodeOutput::VisualShaderNodeOutput() { +} + +/////////////////////////// + +void VisualShaderNodeUniform::set_uniform_name(const String &p_name) { + uniform_name = p_name; + emit_signal("name_changed"); + emit_changed(); +} + +String VisualShaderNodeUniform::get_uniform_name() const { + return uniform_name; +} + +void VisualShaderNodeUniform::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_uniform_name", "name"), &VisualShaderNodeUniform::set_uniform_name); + ClassDB::bind_method(D_METHOD("get_uniform_name"), &VisualShaderNodeUniform::get_uniform_name); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "uniform_name"), "set_uniform_name", "get_uniform_name"); +} + +VisualShaderNodeUniform::VisualShaderNodeUniform() { +} diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h new file mode 100644 index 0000000000..6ff1c9a9fe --- /dev/null +++ b/scene/resources/visual_shader.h @@ -0,0 +1,284 @@ +#ifndef VISUAL_SHADER_H +#define VISUAL_SHADER_H + +#include "scene/resources/shader.h" +#include "string_builder.h" + +class VisualShaderNodeUniform; +class VisualShaderNode; + +class VisualShader : public Shader { + GDCLASS(VisualShader, Shader) +public: + enum Type { + TYPE_VERTEX, + TYPE_FRAGMENT, + TYPE_LIGHT, + TYPE_MAX + }; + + struct Connection { + int from_node; + int from_port; + int to_node; + int to_port; + }; + + struct DefaultTextureParam { + StringName name; + Ref<Texture> param; + }; + +private: + struct Node { + Ref<VisualShaderNode> node; + Vector2 position; + }; + + struct Graph { + Map<int, Node> nodes; + List<Connection> connections; + } graph[TYPE_MAX]; + + Shader::Mode shader_mode; + + Array _get_node_connections(Type p_type) const; + + Vector2 graph_offset; + + struct RenderModeEnums { + Shader::Mode mode; + const char *string; + }; + + HashMap<String, int> modes; + Set<StringName> flags; + + static RenderModeEnums render_mode_enums[]; + + volatile mutable bool dirty; + void _queue_update(); + + union ConnectionKey { + + struct { + uint64_t node : 32; + uint64_t port : 32; + }; + uint64_t key; + bool operator<(const ConnectionKey &p_key) const { + return key < p_key.key; + } + }; + + Error _write_node(Type p_type, StringBuilder &global_code, StringBuilder &code, Vector<DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview) const; + + void _input_type_changed(Type p_type, int p_id); + +protected: + virtual void _update_shader() const; + static void _bind_methods(); + + 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; + +public: + enum { + NODE_ID_INVALID = -1, + NODE_ID_OUTPUT = 0, + }; + + void add_node(Type p_type, const Ref<VisualShaderNode> &p_node, const Vector2 &p_position, int p_id); + void set_node_position(Type p_type, int p_id, const Vector2 &p_position); + + Vector2 get_node_position(Type p_type, int p_id) const; + Ref<VisualShaderNode> get_node(Type p_type, int p_id) const; + + Vector<int> get_node_list(Type p_type) const; + int get_valid_node_id(Type p_type) const; + + int find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const; + void remove_node(Type p_type, int p_id); + + bool is_node_connection(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; + bool can_connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const; + Error connect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + void disconnect_nodes(Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port); + + void get_node_connections(Type p_type, List<Connection> *r_connections) const; + + void set_mode(Mode p_mode); + virtual Mode get_mode() const; + + void set_graph_offset(const Vector2 &p_offset); + Vector2 get_graph_offset() const; + + String generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &r_default_tex_params) const; + + String validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const; + + VisualShader(); +}; + +VARIANT_ENUM_CAST(VisualShader::Type) +/// +/// +/// + +class VisualShaderNode : public Resource { + GDCLASS(VisualShaderNode, Resource) + + int port_preview; + + Map<int, Variant> default_input_values; + + Array _get_default_input_values() const; + void _set_default_input_values(const Array &p_values); + +protected: + static void _bind_methods(); + +public: + enum PortType { + PORT_TYPE_SCALAR, + PORT_TYPE_VECTOR, + PORT_TYPE_TRANSFORM, + }; + + virtual String get_caption() const = 0; + + virtual int get_input_port_count() const = 0; + virtual PortType get_input_port_type(int p_port) const = 0; + virtual String get_input_port_name(int p_port) const = 0; + + void set_input_port_default_value(int p_port, const Variant &p_value); + Variant get_input_port_default_value(int p_port) const; // if NIL (default if node does not set anything) is returned, it means no default value is wanted if disconnected, thus no input var must be supplied (empty string will be supplied) + + virtual int get_output_port_count() const = 0; + virtual PortType get_output_port_type(int p_port) const = 0; + virtual String get_output_port_name(int p_port) const = 0; + + void set_output_port_for_preview(int p_index); + int get_output_port_for_preview() const; + + virtual bool is_port_separator(int p_index) const; + + virtual Vector<StringName> get_editable_properties() const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const = 0; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const; + + VisualShaderNode(); +}; +///// + +class VisualShaderNodeInput : public VisualShaderNode { + GDCLASS(VisualShaderNodeInput, VisualShaderNode) + + friend class VisualShader; + VisualShader::Type shader_type; + Shader::Mode shader_mode; + + struct Port { + Shader::Mode mode; + VisualShader::Type shader_type; + PortType type; + const char *name; + const char *string; + }; + + static const Port ports[]; + static const Port preview_ports[]; + + String input_name; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String get_caption() const; + + virtual String generate_code_for_preview(VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; + + void set_input_name(String p_name); + String get_input_name() const; + + int get_input_index_count() const; + PortType get_input_index_type(int p_index) const; + String get_input_index_name(int p_index) const; + + PortType get_input_type_by_name(String p_name) const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeInput(); +}; + +/// + +class VisualShaderNodeOutput : public VisualShaderNode { + GDCLASS(VisualShaderNodeOutput, VisualShaderNode) +public: + friend class VisualShader; + VisualShader::Type shader_type; + Shader::Mode shader_mode; + + struct Port { + Shader::Mode mode; + VisualShader::Type shader_type; + PortType type; + const char *name; + const char *string; + }; + + static const Port ports[]; + +public: + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + Variant get_input_port_default_value(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual bool is_port_separator(int p_index) const; + + virtual String get_caption() const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; + + VisualShaderNodeOutput(); +}; + +class VisualShaderNodeUniform : public VisualShaderNode { + GDCLASS(VisualShaderNodeUniform, VisualShaderNode) + + String uniform_name; + +protected: + static void _bind_methods(); + +public: + void set_uniform_name(const String &p_name); + String get_uniform_name() const; + + VisualShaderNodeUniform(); +}; + +#endif // VISUAL_SHADER_H diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp new file mode 100644 index 0000000000..98ecdbdf30 --- /dev/null +++ b/scene/resources/visual_shader_nodes.cpp @@ -0,0 +1,1896 @@ +#include "visual_shader_nodes.h" +////////////// Scalar + +String VisualShaderNodeScalarConstant::get_caption() const { + return "Scalar"; +} + +int VisualShaderNodeScalarConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeScalarConstant::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarConstant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + vformat("%.6f", constant) + ";\n"; +} + +void VisualShaderNodeScalarConstant::set_constant(float p_value) { + + constant = p_value; + emit_changed(); +} + +float VisualShaderNodeScalarConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeScalarConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeScalarConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeScalarConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeScalarConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeScalarConstant::VisualShaderNodeScalarConstant() { + constant = 0; +} + +////////////// Color + +String VisualShaderNodeColorConstant::get_caption() const { + return "Color"; +} + +int VisualShaderNodeColorConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeColorConstant::get_output_port_count() const { + return 2; +} +VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const { + return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code; + code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.r, constant.g, constant.b) + ";\n"; + code += "\t" + p_output_vars[1] + " = " + vformat("%.6f", constant.a) + ";\n"; + + return code; +} + +void VisualShaderNodeColorConstant::set_constant(Color p_value) { + + constant = p_value; + emit_changed(); +} + +Color VisualShaderNodeColorConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeColorConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeColorConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeColorConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeColorConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeColorConstant::VisualShaderNodeColorConstant() { + constant = Color(1, 1, 1, 1); +} + +////////////// Vector + +String VisualShaderNodeVec3Constant::get_caption() const { + return "Vector"; +} + +int VisualShaderNodeVec3Constant::get_input_port_count() const { + return 0; +} +VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec3Constant::get_output_port_count() const { + return 1; +} +VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeVec3Constant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f,%.6f,%.6f)", constant.x, constant.y, constant.z) + ";\n"; +} + +void VisualShaderNodeVec3Constant::set_constant(Vector3 p_value) { + + constant = p_value; + emit_changed(); +} + +Vector3 VisualShaderNodeVec3Constant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeVec3Constant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeVec3Constant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeVec3Constant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeVec3Constant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeVec3Constant::VisualShaderNodeVec3Constant() { +} + +////////////// Transform + +String VisualShaderNodeTransformConstant::get_caption() const { + return "Transform"; +} + +int VisualShaderNodeTransformConstant::get_input_port_count() const { + return 0; +} +VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeTransformConstant::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + Transform t = constant; + t.basis.transpose(); + + String code = "\t" + p_output_vars[0] + " = mat4("; + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[0].x, t.basis[0].y, t.basis[0].z); + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[1].x, t.basis[1].y, t.basis[1].z); + code += vformat("vec4(%.6f,%.6f,%.6f,0.0),", t.basis[2].x, t.basis[2].y, t.basis[2].z); + code += vformat("vec4(%.6f,%.6f,%.6f,1.0) );\n", t.origin.x, t.origin.y, t.origin.z); + return code; +} + +void VisualShaderNodeTransformConstant::set_constant(Transform p_value) { + + constant = p_value; + emit_changed(); +} + +Transform VisualShaderNodeTransformConstant::get_constant() const { + + return constant; +} + +Vector<StringName> VisualShaderNodeTransformConstant::get_editable_properties() const { + Vector<StringName> props; + props.push_back("constant"); + return props; +} + +void VisualShaderNodeTransformConstant::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_constant", "value"), &VisualShaderNodeTransformConstant::set_constant); + ClassDB::bind_method(D_METHOD("get_constant"), &VisualShaderNodeTransformConstant::get_constant); + + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "constant"), "set_constant", "get_constant"); +} + +VisualShaderNodeTransformConstant::VisualShaderNodeTransformConstant() { +} + +////////////// Texture + +String VisualShaderNodeTexture::get_caption() const { + return "Texture"; +} + +int VisualShaderNodeTexture::get_input_port_count() const { + return 2; +} +VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTexture::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeTexture::get_output_port_count() const { + return 2; +} +VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTexture::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +static String make_unique_id(VisualShader::Type p_type, int p_id, const String &p_name) { + + static const char *typepf[VisualShader::TYPE_MAX] = { "vtx", "frg", "lgt" }; + return p_name + "_" + String(typepf[p_type]) + "_" + itos(p_id); +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeTexture::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "tex"); + dtp.param = texture; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +String VisualShaderNodeTexture::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + if (source == SOURCE_TEXTURE) { + + String u = "uniform sampler2D " + make_unique_id(p_type, p_id, "tex"); + switch (texture_type) { + case TYPE_DATA: break; + case TYPE_COLOR: u += " : hint_color"; break; + case TYPE_NORMALMAP: u += " : hint_normal"; break; + } + return u + ";"; + } + + return String(); +} + +String VisualShaderNodeTexture::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + if (source == SOURCE_TEXTURE) { + String id = make_unique_id(p_type, p_id, "tex"); + String code; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\tvec4 " + id + "_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; + } + + if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy, 0.0 );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( SCREEN_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = texture( TEXTURE , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 _tex_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 _tex_read = texture( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 _tex_read = textureLod( NORMAL_TEXTURE , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = _tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = _tex_read.a;\n"; + code += "\t}\n"; + return code; + } + + //none + String code; + code += "\t" + p_output_vars[0] + " = vec3(0.0);\n"; + code += "\t" + p_output_vars[1] + " = 1.0;\n"; + return code; +} + +void VisualShaderNodeTexture::set_source(Source p_source) { + source = p_source; + emit_changed(); + emit_signal("editor_refresh_request"); +} + +VisualShaderNodeTexture::Source VisualShaderNodeTexture::get_source() const { + return source; +} + +void VisualShaderNodeTexture::set_texture(Ref<Texture> p_value) { + + texture = p_value; + emit_changed(); +} + +Ref<Texture> VisualShaderNodeTexture::get_texture() const { + + return texture; +} + +void VisualShaderNodeTexture::set_texture_type(TextureType p_type) { + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeTexture::TextureType VisualShaderNodeTexture::get_texture_type() const { + return texture_type; +} + +Vector<StringName> VisualShaderNodeTexture::get_editable_properties() const { + Vector<StringName> props; + props.push_back("source"); + if (source == SOURCE_TEXTURE) { + props.push_back("texture"); + props.push_back("texture_type"); + } + return props; +} + +String VisualShaderNodeTexture::get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const { + + if (source == SOURCE_TEXTURE) { + return String(); // all good + } + + if (source == SOURCE_SCREEN && (p_mode == Shader::MODE_SPATIAL || p_mode == Shader::MODE_CANVAS_ITEM) && p_type == VisualShader::TYPE_FRAGMENT) { + + return String(); // all good + } + + if (source == SOURCE_2D_TEXTURE && p_mode == Shader::MODE_CANVAS_ITEM && p_type == VisualShader::TYPE_FRAGMENT) { + + return String(); // all good + } + + if (source == SOURCE_2D_NORMAL && p_mode == Shader::MODE_CANVAS_ITEM) { + + return String(); // all good + } + + return TTR("Invalid source for shader."); +} + +void VisualShaderNodeTexture::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_source", "value"), &VisualShaderNodeTexture::set_source); + ClassDB::bind_method(D_METHOD("get_source"), &VisualShaderNodeTexture::get_source); + + ClassDB::bind_method(D_METHOD("set_texture", "value"), &VisualShaderNodeTexture::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &VisualShaderNodeTexture::get_texture); + + ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeTexture::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTexture::get_texture_type); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D"), "set_source", "get_source"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + + BIND_ENUM_CONSTANT(SOURCE_TEXTURE); + BIND_ENUM_CONSTANT(SOURCE_SCREEN); + BIND_ENUM_CONSTANT(SOURCE_2D_TEXTURE); + BIND_ENUM_CONSTANT(SOURCE_2D_NORMAL); + BIND_ENUM_CONSTANT(TYPE_DATA); + BIND_ENUM_CONSTANT(TYPE_COLOR); + BIND_ENUM_CONSTANT(TYPE_NORMALMAP); +} + +VisualShaderNodeTexture::VisualShaderNodeTexture() { + texture_type = TYPE_DATA; + source = SOURCE_TEXTURE; +} + +////////////// CubeMap + +String VisualShaderNodeCubeMap::get_caption() const { + return "CubeMap"; +} + +int VisualShaderNodeCubeMap::get_input_port_count() const { + return 2; +} +VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeCubeMap::get_output_port_count() const { + return 2; +} +VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMap::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubeMap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const { + VisualShader::DefaultTextureParam dtp; + dtp.name = make_unique_id(p_type, p_id, "cube"); + dtp.param = cube_map; + Vector<VisualShader::DefaultTextureParam> ret; + ret.push_back(dtp); + return ret; +} + +String VisualShaderNodeCubeMap::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + String u = "uniform sampler2DCube " + make_unique_id(p_type, p_id, "cube"); + switch (texture_type) { + case TYPE_DATA: break; + case TYPE_COLOR: u += " : hint_color"; break; + case TYPE_NORMALMAP: u += " : hint_normal"; break; + } + return u + ";"; +} + +String VisualShaderNodeCubeMap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String id = make_unique_id(p_type, p_id, "cube"); + String code; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\tvec4 " + id + "_read = vec4(0.0);\n"; + + } else if (p_input_vars[1] == String()) { + //no lod + code += "\tvec4 " + id + "_read = texture( " + id + " , " + p_input_vars[0] + " );\n"; + } else { + code += "\tvec4 " + id + "_read = textureLod( " + id + " , " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; + } + + code += "\t" + p_output_vars[0] + " = " + id + "_read.rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + id + "_read.a;\n"; + return code; +} + +void VisualShaderNodeCubeMap::set_cube_map(Ref<CubeMap> p_value) { + + cube_map = p_value; + emit_changed(); +} + +Ref<CubeMap> VisualShaderNodeCubeMap::get_cube_map() const { + + return cube_map; +} + +void VisualShaderNodeCubeMap::set_texture_type(TextureType p_type) { + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeCubeMap::TextureType VisualShaderNodeCubeMap::get_texture_type() const { + return texture_type; +} + +Vector<StringName> VisualShaderNodeCubeMap::get_editable_properties() const { + Vector<StringName> props; + props.push_back("cube_map"); + props.push_back("texture_type"); + return props; +} + +void VisualShaderNodeCubeMap::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_cube_map", "value"), &VisualShaderNodeCubeMap::set_cube_map); + ClassDB::bind_method(D_METHOD("get_cube_map"), &VisualShaderNodeCubeMap::get_cube_map); + + ClassDB::bind_method(D_METHOD("set_texture_type", "value"), &VisualShaderNodeCubeMap::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeCubeMap::get_texture_type); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "CubeMap"), "set_cube_map", "get_cube_map"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type"); + + BIND_ENUM_CONSTANT(TYPE_DATA); + BIND_ENUM_CONSTANT(TYPE_COLOR); + BIND_ENUM_CONSTANT(TYPE_NORMALMAP); +} + +VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() { + texture_type = TYPE_DATA; +} +////////////// Scalar Op + +String VisualShaderNodeScalarOp::get_caption() const { + return "ScalarOp"; +} + +int VisualShaderNodeScalarOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeScalarOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code = "\t" + p_output_vars[0] + " = "; + switch (op) { + + case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break; + case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break; + case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break; + case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break; + case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_ATAN2: code += "atan( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + } + + return code; +} + +void VisualShaderNodeScalarOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeScalarOp::Operator VisualShaderNodeScalarOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeScalarOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeScalarOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeScalarOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeScalarOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_ADD); + BIND_ENUM_CONSTANT(OP_SUB); + BIND_ENUM_CONSTANT(OP_MUL); + BIND_ENUM_CONSTANT(OP_DIV); + BIND_ENUM_CONSTANT(OP_MOD); + BIND_ENUM_CONSTANT(OP_POW); + BIND_ENUM_CONSTANT(OP_MAX); + BIND_ENUM_CONSTANT(OP_MIN); + BIND_ENUM_CONSTANT(OP_ATAN2); +} + +VisualShaderNodeScalarOp::VisualShaderNodeScalarOp() { + op = OP_ADD; + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); +} + +////////////// Vector Op + +String VisualShaderNodeVectorOp::get_caption() const { + return "VectorOp"; +} + +int VisualShaderNodeVectorOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeVectorOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeVectorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code = "\t" + p_output_vars[0] + " = "; + switch (op) { + + case OP_ADD: code += p_input_vars[0] + " + " + p_input_vars[1] + ";\n"; break; + case OP_SUB: code += p_input_vars[0] + " - " + p_input_vars[1] + ";\n"; break; + case OP_MUL: code += p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; break; + case OP_DIV: code += p_input_vars[0] + " / " + p_input_vars[1] + ";\n"; break; + case OP_MOD: code += "mod( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_POW: code += "pow( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MAX: code += "max( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_MIN: code += "min( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + case OP_CROSS: code += "cross( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; break; + } + + return code; +} + +void VisualShaderNodeVectorOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeVectorOp::Operator VisualShaderNodeVectorOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeVectorOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeVectorOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_ADD); + BIND_ENUM_CONSTANT(OP_SUB); + BIND_ENUM_CONSTANT(OP_MUL); + BIND_ENUM_CONSTANT(OP_DIV); + BIND_ENUM_CONSTANT(OP_MOD); + BIND_ENUM_CONSTANT(OP_POW); + BIND_ENUM_CONSTANT(OP_MAX); + BIND_ENUM_CONSTANT(OP_MIN); + BIND_ENUM_CONSTANT(OP_CROSS); +} + +VisualShaderNodeVectorOp::VisualShaderNodeVectorOp() { + op = OP_ADD; + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Color Op + +String VisualShaderNodeColorOp::get_caption() const { + return "ColorOp"; +} + +int VisualShaderNodeColorOp::get_input_port_count() const { + return 2; +} +VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorOp::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeColorOp::get_output_port_count() const { + return 1; +} +VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorOp::get_output_port_name(int p_port) const { + return "op"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorOp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String code; + static const char *axisn[3] = { "x", "y", "z" }; + switch (op) { + case OP_SCREEN: { + + code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")*(vec3(1.0)-" + p_input_vars[1] + ");\n"; + } break; + case OP_DIFFERENCE: { + + code += "\t" + p_output_vars[0] + "=abs(" + p_input_vars[0] + "-" + p_input_vars[1] + ");\n"; + } break; + case OP_DARKEN: { + + code += "\t" + p_output_vars[0] + "=min(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n"; + } break; + case OP_LIGHTEN: { + + code += "\t" + p_output_vars[0] + "=max(" + p_input_vars[0] + "," + p_input_vars[1] + ");\n"; + + } break; + case OP_OVERLAY: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 2.0 * base * blend;\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = 1.0 - 2.0 * (1.0 - blend) * (1.0 - base);\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + case OP_DODGE: { + + code += "\t" + p_output_vars[0] + "=(" + p_input_vars[0] + ")/(vec3(1.0)-" + p_input_vars[1] + ");\n"; + + } break; + case OP_BURN: { + + code += "\t" + p_output_vars[0] + "=vec3(1.0)-(vec3(1.0)-" + p_input_vars[0] + ")/(" + p_input_vars[1] + ");\n"; + } break; + case OP_SOFT_LIGHT: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (blend+0.5));\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-(blend-0.5)));\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + case OP_HARD_LIGHT: { + + for (int i = 0; i < 3; i++) { + code += "\t{\n"; + code += "\t\tfloat base=" + p_input_vars[0] + "." + axisn[i] + ";\n"; + code += "\t\tfloat blend=" + p_input_vars[1] + "." + axisn[i] + ";\n"; + code += "\t\tif (base < 0.5) {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (base * (2.0*blend));\n"; + code += "\t\t} else {\n"; + code += "\t\t\t" + p_output_vars[0] + "." + axisn[i] + " = (1.0 - (1.0-base) * (1.0-2.0*(blend-0.5)));\n"; + code += "\t\t}\n"; + code += "\t}\n"; + } + + } break; + } + + return code; +} + +void VisualShaderNodeColorOp::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeColorOp::Operator VisualShaderNodeColorOp::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeColorOp::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeColorOp::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_SCREEN); + BIND_ENUM_CONSTANT(OP_DIFFERENCE); + BIND_ENUM_CONSTANT(OP_DARKEN); + BIND_ENUM_CONSTANT(OP_LIGHTEN); + BIND_ENUM_CONSTANT(OP_OVERLAY); + BIND_ENUM_CONSTANT(OP_DODGE); + BIND_ENUM_CONSTANT(OP_BURN); + BIND_ENUM_CONSTANT(OP_SOFT_LIGHT); + BIND_ENUM_CONSTANT(OP_HARD_LIGHT); +} + +VisualShaderNodeColorOp::VisualShaderNodeColorOp() { + op = OP_SCREEN; + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Transform Mult + +String VisualShaderNodeTransformMult::get_caption() const { + return "TransformMult"; +} + +int VisualShaderNodeTransformMult::get_input_port_count() const { + return 2; +} +VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeTransformMult::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const { + return "mult"; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + if (op == OP_AxB) { + return "\t" + p_output_vars[0] + " = " + p_input_vars[0] + " * " + p_input_vars[1] + ";\n"; + } else { + return "\t" + p_output_vars[0] + " = " + p_input_vars[1] + " * " + p_input_vars[0] + ";\n"; + } +} + +void VisualShaderNodeTransformMult::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeTransformMult::Operator VisualShaderNodeTransformMult::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeTransformMult::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeTransformMult::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformMult::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformMult::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_AxB); + BIND_ENUM_CONSTANT(OP_BxA); +} + +VisualShaderNodeTransformMult::VisualShaderNodeTransformMult() { + op = OP_AxB; + set_input_port_default_value(0, Transform()); + set_input_port_default_value(1, Transform()); +} + +////////////// TransformVec Mult + +String VisualShaderNodeTransformVecMult::get_caption() const { + return "TransformVectorMult"; +} + +int VisualShaderNodeTransformVecMult::get_input_port_count() const { + return 2; +} +VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeTransformVecMult::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeTransformVecMult::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + if (op == OP_AxB) { + return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 1.0) ).xyz;\n"; + } else if (op == OP_BxA) { + return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 1.0) * " + p_input_vars[0] + " ).xyz;\n"; + } else if (op == OP_3x3_AxB) { + return "\t" + p_output_vars[0] + " = ( " + p_input_vars[0] + " * vec4(" + p_input_vars[1] + ", 0.0) ).xyz;\n"; + } else { + return "\t" + p_output_vars[0] + " = ( vec4(" + p_input_vars[1] + ", 0.0) * " + p_input_vars[0] + " ).xyz;\n"; + } +} + +void VisualShaderNodeTransformVecMult::set_operator(Operator p_op) { + + op = p_op; + emit_changed(); +} + +VisualShaderNodeTransformVecMult::Operator VisualShaderNodeTransformVecMult::get_operator() const { + + return op; +} + +Vector<StringName> VisualShaderNodeTransformVecMult::get_editable_properties() const { + Vector<StringName> props; + props.push_back("operator"); + return props; +} + +void VisualShaderNodeTransformVecMult::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeTransformVecMult::set_operator); + ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeTransformVecMult::get_operator); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "A x B,B x A,A x B (3x3),B x A (3x3)"), "set_operator", "get_operator"); + + BIND_ENUM_CONSTANT(OP_AxB); + BIND_ENUM_CONSTANT(OP_BxA); + BIND_ENUM_CONSTANT(OP_3x3_AxB); + BIND_ENUM_CONSTANT(OP_3x3_BxA); +} + +VisualShaderNodeTransformVecMult::VisualShaderNodeTransformVecMult() { + op = OP_AxB; + set_input_port_default_value(0, Transform()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Scalar Func + +String VisualShaderNodeScalarFunc::get_caption() const { + return "ScalarFunc"; +} + +int VisualShaderNodeScalarFunc::get_input_port_count() const { + return 1; +} +VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeScalarFunc::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + static const char *scalar_func_id[FUNC_NEGATE + 1] = { + "sin($)", + "cos($)", + "tan($)", + "asin($)", + "acos($)", + "atan($)", + "sinh($)", + "cosh($)", + "tanh($)", + "log($)", + "exp($)", + "sqrt($)", + "abs($)", + "sign($)", + "floor($)", + "round($)", + "ceil($)", + "fract($)", + "min(max($,0),1)", + "-($)", + }; + + return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; +} + +void VisualShaderNodeScalarFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeScalarFunc::Function VisualShaderNodeScalarFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeScalarFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeScalarFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_SIN); + BIND_ENUM_CONSTANT(FUNC_COS); + BIND_ENUM_CONSTANT(FUNC_TAN); + BIND_ENUM_CONSTANT(FUNC_ASIN); + BIND_ENUM_CONSTANT(FUNC_ACOS); + BIND_ENUM_CONSTANT(FUNC_ATAN); + BIND_ENUM_CONSTANT(FUNC_SINH); + BIND_ENUM_CONSTANT(FUNC_COSH); + BIND_ENUM_CONSTANT(FUNC_TANH); + BIND_ENUM_CONSTANT(FUNC_LOG); + BIND_ENUM_CONSTANT(FUNC_EXP); + BIND_ENUM_CONSTANT(FUNC_SQRT); + BIND_ENUM_CONSTANT(FUNC_ABS); + BIND_ENUM_CONSTANT(FUNC_SIGN); + BIND_ENUM_CONSTANT(FUNC_FLOOR); + BIND_ENUM_CONSTANT(FUNC_ROUND); + BIND_ENUM_CONSTANT(FUNC_CEIL); + BIND_ENUM_CONSTANT(FUNC_FRAC); + BIND_ENUM_CONSTANT(FUNC_SATURATE); + BIND_ENUM_CONSTANT(FUNC_NEGATE); +} + +VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() { + func = FUNC_SIGN; + set_input_port_default_value(0, 0.0); +} + +////////////// Vector Func + +String VisualShaderNodeVectorFunc::get_caption() const { + return "VectorFunc"; +} + +int VisualShaderNodeVectorFunc::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVectorFunc::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + static const char *vec_func_id[FUNC_HSV2RGB + 1] = { + "normalize($)", + "max(min($,vec3(1.0)),vec3(0.0))", + "-($)", + "1.0/($)", + "", + "", + }; + + String code; + + if (func == FUNC_RGB2HSV) { + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tvec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n"; + code += "\t\tvec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n"; + code += "\t\tvec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n"; + code += "\t\tfloat d = q.x - min(q.w, q.y);\n"; + code += "\t\tfloat e = 1.0e-10;\n"; + code += "\t\t" + p_output_vars[0] + "=vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n"; + code += "\t}\n"; + } else if (func == FUNC_HSV2RGB) { + code += "\t{\n"; + code += "\t\tvec3 c = " + p_input_vars[0] + ";\n"; + code += "\t\tvec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n"; + code += "\t\tvec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n"; + code += "\t\t" + p_output_vars[0] + "=c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"; + code += "\t}\n"; + + } else { + code += "\t" + p_output_vars[0] + "=" + String(vec_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; + } + + return code; +} + +void VisualShaderNodeVectorFunc::set_function(Function p_func) { + + func = p_func; + emit_changed(); +} + +VisualShaderNodeVectorFunc::Function VisualShaderNodeVectorFunc::get_function() const { + + return func; +} + +Vector<StringName> VisualShaderNodeVectorFunc::get_editable_properties() const { + Vector<StringName> props; + props.push_back("function"); + return props; +} + +void VisualShaderNodeVectorFunc::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); + ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB"), "set_function", "get_function"); + + BIND_ENUM_CONSTANT(FUNC_NORMALIZE); + BIND_ENUM_CONSTANT(FUNC_SATURATE); + BIND_ENUM_CONSTANT(FUNC_NEGATE); + BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); + BIND_ENUM_CONSTANT(FUNC_RGB2HSV); + BIND_ENUM_CONSTANT(FUNC_HSV2RGB); +} + +VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { + func = FUNC_NORMALIZE; + set_input_port_default_value(0, Vector3()); +} + +////////////// Dot Product + +String VisualShaderNodeDotProduct::get_caption() const { + return "DotProduct"; +} + +int VisualShaderNodeDotProduct::get_input_port_count() const { + return 2; +} +VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const { + return p_port == 0 ? "a" : "b"; +} + +int VisualShaderNodeDotProduct::get_output_port_count() const { + return 1; +} +VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const { + return "dot"; +} + +String VisualShaderNodeDotProduct::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = dot( " + p_input_vars[0] + " , " + p_input_vars[1] + " );\n"; +} + +VisualShaderNodeDotProduct::VisualShaderNodeDotProduct() { + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); +} + +////////////// Vector Len + +String VisualShaderNodeVectorLen::get_caption() const { + return "VectorLen"; +} + +int VisualShaderNodeVectorLen::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const { + return ""; +} + +int VisualShaderNodeVectorLen::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const { + return "length"; +} + +String VisualShaderNodeVectorLen::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = length( " + p_input_vars[0] + " );\n"; +} + +VisualShaderNodeVectorLen::VisualShaderNodeVectorLen() { + set_input_port_default_value(0, Vector3()); +} + +////////////// Scalar Interp + +String VisualShaderNodeScalarInterp::get_caption() const { + return "ScalarInterp"; +} + +int VisualShaderNodeScalarInterp::get_input_port_count() const { + return 3; +} +VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarInterp::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "a"; + } else if (p_port == 1) { + return "b"; + } else { + return "c"; + } +} + +int VisualShaderNodeScalarInterp::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarInterp::PortType VisualShaderNodeScalarInterp::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarInterp::get_output_port_name(int p_port) const { + return "mix"; +} + +String VisualShaderNodeScalarInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeScalarInterp::VisualShaderNodeScalarInterp() { + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +////////////// Vector Interp + +String VisualShaderNodeVectorInterp::get_caption() const { + return "VectorInterp"; +} + +int VisualShaderNodeVectorInterp::get_input_port_count() const { + return 3; +} +VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorInterp::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "a"; + } else if (p_port == 1) { + return "b"; + } else { + return "c"; + } +} + +int VisualShaderNodeVectorInterp::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorInterp::PortType VisualShaderNodeVectorInterp::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorInterp::get_output_port_name(int p_port) const { + return "mix"; +} + +String VisualShaderNodeVectorInterp::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mix( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorInterp::VisualShaderNodeVectorInterp() { + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); + set_input_port_default_value(2, Vector3()); +} + +////////////// Vector Compose +String VisualShaderNodeVectorCompose::get_caption() const { + return "VectorCompose"; +} + +int VisualShaderNodeVectorCompose::get_input_port_count() const { + return 3; +} +VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else { + return "z"; + } +} + +int VisualShaderNodeVectorCompose::get_output_port_count() const { + return 1; +} +VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const { + return "vec"; +} + +String VisualShaderNodeVectorCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = vec3( " + p_input_vars[0] + " , " + p_input_vars[1] + " , " + p_input_vars[2] + " );\n"; +} + +VisualShaderNodeVectorCompose::VisualShaderNodeVectorCompose() { + + set_input_port_default_value(0, 0.0); + set_input_port_default_value(1, 0.0); + set_input_port_default_value(2, 0.0); +} + +////////////// Transform Compose + +String VisualShaderNodeTransformCompose::get_caption() const { + return "TransformCompose"; +} + +int VisualShaderNodeTransformCompose::get_input_port_count() const { + return 4; +} +VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else if (p_port == 2) { + return "z"; + } else { + return "origin"; + } +} + +int VisualShaderNodeTransformCompose::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const { + return "xform"; +} + +String VisualShaderNodeTransformCompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = mat4( vec4(" + p_input_vars[0] + ", 0.0) , vec4(" + p_input_vars[1] + ", 0.0) , vec4(" + p_input_vars[2] + ",0.0), vec4(" + p_input_vars[3] + ",1.0) );\n"; +} + +VisualShaderNodeTransformCompose::VisualShaderNodeTransformCompose() { + + set_input_port_default_value(0, Vector3()); + set_input_port_default_value(1, Vector3()); + set_input_port_default_value(2, Vector3()); + set_input_port_default_value(3, Vector3()); +} + +////////////// Vector Decompose +String VisualShaderNodeVectorDecompose::get_caption() const { + return "VectorDecompose"; +} + +int VisualShaderNodeVectorDecompose::get_input_port_count() const { + return 1; +} +VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const { + return "vec"; +} + +int VisualShaderNodeVectorDecompose::get_output_port_count() const { + return 3; +} +VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else { + return "z"; + } +} + +String VisualShaderNodeVectorDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + String code; + code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + ".x;\n"; + code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + ".y;\n"; + code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + ".z;\n"; + return code; +} + +VisualShaderNodeVectorDecompose::VisualShaderNodeVectorDecompose() { + set_input_port_default_value(0, Vector3()); +} + +////////////// Transform Decompose + +String VisualShaderNodeTransformDecompose::get_caption() const { + return "TransformDecompose"; +} + +int VisualShaderNodeTransformDecompose::get_input_port_count() const { + return 1; +} +VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_input_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformDecompose::get_input_port_name(int p_port) const { + return "xform"; +} + +int VisualShaderNodeTransformDecompose::get_output_port_count() const { + return 4; +} +VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const { + if (p_port == 0) { + return "x"; + } else if (p_port == 1) { + return "y"; + } else if (p_port == 2) { + return "z"; + } else { + return "origin"; + } +} + +String VisualShaderNodeTransformDecompose::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + String code; + code += "\t" + p_output_vars[0] + " = " + p_input_vars[0] + "[0].xyz;\n"; + code += "\t" + p_output_vars[1] + " = " + p_input_vars[0] + "[1].xyz;\n"; + code += "\t" + p_output_vars[2] + " = " + p_input_vars[0] + "[2].xyz;\n"; + code += "\t" + p_output_vars[3] + " = " + p_input_vars[0] + "[3].xyz;\n"; + return code; +} + +VisualShaderNodeTransformDecompose::VisualShaderNodeTransformDecompose() { + set_input_port_default_value(0, Transform()); +} + +////////////// Scalar Uniform + +String VisualShaderNodeScalarUniform::get_caption() const { + return "ScalarUniform"; +} + +int VisualShaderNodeScalarUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeScalarUniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} +String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} + +String VisualShaderNodeScalarUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform float " + get_uniform_name() + ";\n"; +} +String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeScalarUniform::VisualShaderNodeScalarUniform() { +} + +////////////// Color Uniform + +String VisualShaderNodeColorUniform::get_caption() const { + return "ColorUniform"; +} + +int VisualShaderNodeColorUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeColorUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port +} + +String VisualShaderNodeColorUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + + return "uniform vec4 " + get_uniform_name() + " : hint_color;\n"; +} + +String VisualShaderNodeColorUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + String code = "\t" + p_output_vars[0] + " = " + get_uniform_name() + ".rgb;\n"; + code += "\t" + p_output_vars[1] + " = " + get_uniform_name() + ".a;\n"; + return code; +} + +VisualShaderNodeColorUniform::VisualShaderNodeColorUniform() { +} + +////////////// Vector Uniform + +String VisualShaderNodeVec3Uniform::get_caption() const { + return "VectorUniform"; +} + +int VisualShaderNodeVec3Uniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeVec3Uniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} +String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform vec3 " + get_uniform_name() + ";\n"; +} + +String VisualShaderNodeVec3Uniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeVec3Uniform::VisualShaderNodeVec3Uniform() { +} + +////////////// Transform Uniform + +String VisualShaderNodeTransformUniform::get_caption() const { + return "TransformUniform"; +} + +int VisualShaderNodeTransformUniform::get_input_port_count() const { + return 0; +} +VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const { + return PORT_TYPE_VECTOR; +} +String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const { + return String(); +} + +int VisualShaderNodeTransformUniform::get_output_port_count() const { + return 1; +} +VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const { + return PORT_TYPE_TRANSFORM; +} +String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const { + return ""; //no output port means the editor will be used as port +} +String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + return "uniform mat4 " + get_uniform_name() + ";\n"; +} + +String VisualShaderNodeTransformUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; +} + +VisualShaderNodeTransformUniform::VisualShaderNodeTransformUniform() { +} + +////////////// Texture Uniform + +String VisualShaderNodeTextureUniform::get_caption() const { + return "TextureUniform"; +} + +int VisualShaderNodeTextureUniform::get_input_port_count() const { + return 2; +} +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { + return p_port == 0 ? "uv" : "lod"; +} + +int VisualShaderNodeTextureUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +String VisualShaderNodeTextureUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { + String code = "uniform sampler2D " + get_uniform_name(); + + switch (texture_type) { + case TYPE_DATA: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black;\n"; + else + code += ";\n"; + break; + case TYPE_COLOR: + if (color_default == COLOR_DEFAULT_BLACK) + code += " : hint_black_albedo;\n"; + else + code += " : hint_albedo;\n"; + break; + case TYPE_NORMALMAP: code += " : hint_normal;\n"; break; + case TYPE_ANISO: code += " : hint_aniso;\n"; break; + } + + return code; +} + +String VisualShaderNodeTextureUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + + String id = get_uniform_name(); + String code = "\t{\n"; + if (p_input_vars[0] == String()) { //none bound, do nothing + + code += "\t\tvec4 n_tex_read = vec4(0.0);\n"; + } else if (p_input_vars[1] == String()) { + //no lod + code += "\t\tvec4 n_tex_read = texture( " + id + " , " + p_input_vars[0] + ".xy );\n"; + } else { + code += "\t\tvec4 n_tex_read = textureLod( " + id + " , " + p_input_vars[0] + ".xy , " + p_input_vars[1] + " );\n"; + } + + code += "\t\t" + p_output_vars[0] + " = n_tex_read.rgb;\n"; + code += "\t\t" + p_output_vars[1] + " = n_tex_read.a;\n"; + code += "\t}\n"; + return code; +} + +void VisualShaderNodeTextureUniform::set_texture_type(TextureType p_type) { + + texture_type = p_type; + emit_changed(); +} + +VisualShaderNodeTextureUniform::TextureType VisualShaderNodeTextureUniform::get_texture_type() const { + return texture_type; +} + +void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) { + color_default = p_default; + emit_changed(); +} +VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const { + return color_default; +} + +Vector<StringName> VisualShaderNodeTextureUniform::get_editable_properties() const { + Vector<StringName> props; + props.push_back("texture_type"); + props.push_back("color_default"); + return props; +} + +void VisualShaderNodeTextureUniform::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_texture_type", "type"), &VisualShaderNodeTextureUniform::set_texture_type); + ClassDB::bind_method(D_METHOD("get_texture_type"), &VisualShaderNodeTextureUniform::get_texture_type); + + ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default); + ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default"); + + BIND_ENUM_CONSTANT(TYPE_DATA); + BIND_ENUM_CONSTANT(TYPE_COLOR); + BIND_ENUM_CONSTANT(TYPE_NORMALMAP); + BIND_ENUM_CONSTANT(TYPE_ANISO); + + BIND_ENUM_CONSTANT(COLOR_DEFAULT_WHITE); + BIND_ENUM_CONSTANT(COLOR_DEFAULT_BLACK); +} + +VisualShaderNodeTextureUniform::VisualShaderNodeTextureUniform() { + texture_type = TYPE_DATA; + color_default = COLOR_DEFAULT_WHITE; +} + +////////////// CubeMap Uniform + +String VisualShaderNodeCubeMapUniform::get_caption() const { + return "CubeMapUniform"; +} + +int VisualShaderNodeCubeMapUniform::get_input_port_count() const { + return 2; +} +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { + return p_port == 0 ? "normal" : "lod"; +} + +int VisualShaderNodeCubeMapUniform::get_output_port_count() const { + return 2; +} +VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { + return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; +} +String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { + return p_port == 0 ? "rgb" : "alpha"; +} + +String VisualShaderNodeCubeMapUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const { + return String(); +} + +VisualShaderNodeCubeMapUniform::VisualShaderNodeCubeMapUniform() { +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h new file mode 100644 index 0000000000..2ede36fbc8 --- /dev/null +++ b/scene/resources/visual_shader_nodes.h @@ -0,0 +1,861 @@ +#ifndef VISUAL_SHADER_NODES_H +#define VISUAL_SHADER_NODES_H + +#include "scene/resources/visual_shader.h" + +/// CONSTANTS /// + +class VisualShaderNodeScalarConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarConstant, VisualShaderNode) + float constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(float p_value); + float get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarConstant(); +}; + +class VisualShaderNodeColorConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeColorConstant, VisualShaderNode) + Color constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Color p_value); + Color get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeColorConstant(); +}; + +class VisualShaderNodeVec3Constant : public VisualShaderNode { + GDCLASS(VisualShaderNodeVec3Constant, VisualShaderNode) + Vector3 constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Vector3 p_value); + Vector3 get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVec3Constant(); +}; + +class VisualShaderNodeTransformConstant : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformConstant, VisualShaderNode) + Transform constant; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_constant(Transform p_value); + Transform get_constant() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformConstant(); +}; + +////////////////////////////////// + +class VisualShaderNodeTexture : public VisualShaderNode { + GDCLASS(VisualShaderNodeTexture, VisualShaderNode) + Ref<Texture> texture; + +public: + enum Source { + SOURCE_TEXTURE, + SOURCE_SCREEN, + SOURCE_2D_TEXTURE, + SOURCE_2D_NORMAL + }; + + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP + }; + +private: + Source source; + TextureType texture_type; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_source(Source p_source); + Source get_source() const; + + void set_texture(Ref<Texture> p_value); + Ref<Texture> get_texture() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + virtual Vector<StringName> get_editable_properties() const; + + virtual String get_warning(Shader::Mode p_mode, VisualShader::Type p_type) const; + + VisualShaderNodeTexture(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTexture::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeTexture::Source) + +////////////////////////////////// + +class VisualShaderNodeCubeMap : public VisualShaderNode { + GDCLASS(VisualShaderNodeCubeMap, VisualShaderNode) + Ref<CubeMap> cube_map; + +public: + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP + }; + +private: + TextureType texture_type; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const; + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_cube_map(Ref<CubeMap> p_value); + Ref<CubeMap> get_cube_map() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeCubeMap(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeCubeMap::TextureType) +/////////////////////////////////////// + +class VisualShaderNodeScalarOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarOp, VisualShaderNode) + +public: + enum Operator { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_POW, + OP_MAX, + OP_MIN, + OP_ATAN2 + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeScalarOp::Operator) + +class VisualShaderNodeVectorOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorOp, VisualShaderNode) + +public: + enum Operator { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_MOD, + OP_POW, + OP_MAX, + OP_MIN, + OP_CROSS + + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVectorOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeVectorOp::Operator) + +class VisualShaderNodeColorOp : public VisualShaderNode { + GDCLASS(VisualShaderNodeColorOp, VisualShaderNode) + +public: + enum Operator { + OP_SCREEN, + OP_DIFFERENCE, + OP_DARKEN, + OP_LIGHTEN, + OP_OVERLAY, + OP_DODGE, + OP_BURN, + OP_SOFT_LIGHT, + OP_HARD_LIGHT, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeColorOp(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeColorOp::Operator) + +class VisualShaderNodeTransformMult : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformMult, VisualShaderNode) + +public: + enum Operator { + OP_AxB, + OP_BxA, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformMult(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTransformMult::Operator) + +class VisualShaderNodeTransformVecMult : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformVecMult, VisualShaderNode) + +public: + enum Operator { + OP_AxB, + OP_BxA, + OP_3x3_AxB, + OP_3x3_BxA, + }; + +protected: + Operator op; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_operator(Operator p_op); + Operator get_operator() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeTransformVecMult(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTransformVecMult::Operator) + +/////////////////////////////////////// + +class VisualShaderNodeScalarFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarFunc, VisualShaderNode) + +public: + enum Function { + FUNC_SIN, + FUNC_COS, + FUNC_TAN, + FUNC_ASIN, + FUNC_ACOS, + FUNC_ATAN, + FUNC_SINH, + FUNC_COSH, + FUNC_TANH, + FUNC_LOG, + FUNC_EXP, + FUNC_SQRT, + FUNC_ABS, + FUNC_SIGN, + FUNC_FLOOR, + FUNC_ROUND, + FUNC_CEIL, + FUNC_FRAC, + FUNC_SATURATE, + FUNC_NEGATE, + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_func); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeScalarFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeScalarFunc::Function) + +/////////////////////////////////////// + +class VisualShaderNodeVectorFunc : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorFunc, VisualShaderNode) + +public: + enum Function { + FUNC_NORMALIZE, + FUNC_SATURATE, + FUNC_NEGATE, + FUNC_RECIPROCAL, + FUNC_RGB2HSV, + FUNC_HSV2RGB, + }; + +protected: + Function func; + + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + void set_function(Function p_func); + Function get_function() const; + + virtual Vector<StringName> get_editable_properties() const; + + VisualShaderNodeVectorFunc(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeVectorFunc::Function) + +/////////////////////////////////////// + +class VisualShaderNodeDotProduct : public VisualShaderNode { + GDCLASS(VisualShaderNodeDotProduct, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeDotProduct(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorLen : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorLen, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorLen(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeScalarInterp : public VisualShaderNode { + GDCLASS(VisualShaderNodeScalarInterp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarInterp(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorInterp : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorInterp, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorInterp(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorCompose : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorCompose, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorCompose(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeTransformCompose : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformCompose, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformCompose(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeVectorDecompose : public VisualShaderNode { + GDCLASS(VisualShaderNodeVectorDecompose, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVectorDecompose(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeTransformDecompose : public VisualShaderNode { + GDCLASS(VisualShaderNodeTransformDecompose, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformDecompose(); +}; + +/////////////////////////////////////// + +class VisualShaderNodeScalarUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeScalarUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeScalarUniform(); +}; + +class VisualShaderNodeColorUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeColorUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeColorUniform(); +}; + +class VisualShaderNodeVec3Uniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeVec3Uniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeVec3Uniform(); +}; + +class VisualShaderNodeTransformUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeTransformUniform, VisualShaderNodeUniform) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeTransformUniform(); +}; + +////////////////////////////////// + +class VisualShaderNodeTextureUniform : public VisualShaderNodeUniform { + GDCLASS(VisualShaderNodeTextureUniform, VisualShaderNodeUniform) +public: + enum TextureType { + TYPE_DATA, + TYPE_COLOR, + TYPE_NORMALMAP, + TYPE_ANISO, + }; + + enum ColorDefault { + COLOR_DEFAULT_WHITE, + COLOR_DEFAULT_BLACK + }; + +private: + TextureType texture_type; + ColorDefault color_default; + +protected: + static void _bind_methods(); + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const; + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + Vector<StringName> get_editable_properties() const; + + void set_texture_type(TextureType p_type); + TextureType get_texture_type() const; + + void set_color_default(ColorDefault p_default); + ColorDefault get_color_default() const; + + VisualShaderNodeTextureUniform(); +}; + +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::TextureType) +VARIANT_ENUM_CAST(VisualShaderNodeTextureUniform::ColorDefault) + +////////////////////////////////// + +class VisualShaderNodeCubeMapUniform : public VisualShaderNode { + GDCLASS(VisualShaderNodeCubeMapUniform, VisualShaderNode) + +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars) const; //if no output is connected, the output var passed will be empty. if no input is connected and input is NIL, the input var passed will be empty + + VisualShaderNodeCubeMapUniform(); +}; + +#endif // VISUAL_SHADER_NODES_H diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index bf765385d0..661606c7ef 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -102,6 +102,8 @@ SceneStringNames::SceneStringNames() { _update_scroll = StaticCString::create("_update_scroll"); _update_xform = StaticCString::create("_update_xform"); + _clips_input = StaticCString::create("_clips_input"); + _proxgroup_add = StaticCString::create("_proxgroup_add"); _proxgroup_remove = StaticCString::create("_proxgroup_remove"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index b88cf7d8d7..817158f9f3 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -127,6 +127,8 @@ public: StringName _update_scroll; StringName _update_xform; + StringName _clips_input; + StringName _proxgroup_add; StringName _proxgroup_remove; |