diff options
Diffstat (limited to 'scene')
62 files changed, 5458 insertions, 171 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index e3d1592be0..b56eedabc7 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); @@ -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..f6586aff36 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; diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index d05c818ae1..cabd7fddc2 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: { @@ -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/line_2d.cpp b/scene/2d/line_2d.cpp index 3e61dd05f4..e9e895b5bb 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -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..74ad3e79d0 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; @@ -292,6 +294,9 @@ void LineBuilder::build() { 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); @@ -378,6 +383,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 +393,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/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..8787a2c735 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -32,8 +32,8 @@ #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) { /* @@ -971,11 +971,11 @@ RigidBody2D::~RigidBody2D() { ////////////////////////// -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 +989,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 +1039,36 @@ 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; } 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 +1077,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 + + 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)) { //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 +1147,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; @@ -1138,10 +1233,60 @@ Ref<KinematicCollision2D> KinematicBody2D::_get_slide_collision(int 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 +1301,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 +1318,7 @@ KinematicBody2D::KinematicBody2D() : on_floor = false; on_ceiling = false; on_wall = false; + sync_to_physics = false; } KinematicBody2D::~KinematicBody2D() { if (motion_cache.is_valid()) { diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 0fda3c5c05..7bda6ce817 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -276,6 +276,7 @@ public: Vector2 normal; Vector2 collider_vel; ObjectID collider; + RID collider_rid; int collider_shape; Variant collider_metadata; Vector2 remainder; @@ -287,29 +288,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 +330,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/tile_map.cpp b/scene/2d/tile_map.cpp index 9a343ca0f0..72c3ed7425 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; @@ -721,7 +721,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q, bool updat return; if (update) { - _update_dirty_quadrants(); + call_deferred("update_dirty_quadrants"); } } @@ -1026,7 +1026,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() { @@ -1630,7 +1630,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..c8aceac17d 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(); @@ -251,6 +250,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 d46231a677..5f0ac3dd80 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -991,6 +991,7 @@ AudioStreamPlayer3D::AudioStreamPlayer3D() { 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/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..9a2046991b 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -613,6 +613,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/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/physics_body.h b/scene/3d/physics_body.h index 0190dcbfc3..7236eba685 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -294,7 +294,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); 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/spatial.cpp b/scene/3d/spatial.cpp index 748aa8aad4..9b27faed6a 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -280,6 +280,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 +471,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) @@ -735,6 +748,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 +770,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); @@ -797,6 +803,7 @@ void Spatial::_bind_methods() { 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("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 +824,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..653714dc98 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -92,6 +92,7 @@ class Spatial : public Node { bool notify_transform; bool visible; + bool disable_scale; #ifdef TOOLS_ENABLED Ref<SpatialGizmo> gizmo; @@ -153,6 +154,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/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index 670df5cc7f..ba2807d4e8 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; \ @@ -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 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..bba25d64d9 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()); 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_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index c5ad980806..36587a1e91 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() { diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index eac2c8d0c1..111620dac1 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -549,6 +549,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..4fa66e8ede 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -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/gui/control.cpp b/scene/gui/control.cpp index 068af42260..17c349858f 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2188,10 +2188,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 +2827,7 @@ 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"))); ADD_GROUP("Anchor", "anchor_"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.01"), "_set_anchor", "get_anchor", MARGIN_LEFT); diff --git a/scene/gui/control.h b/scene/gui/control.h index fa5274d854..94231867d7 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -454,6 +454,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/item_list.cpp b/scene/gui/item_list.cpp index 72ed0e9b81..aa52739b0a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -285,7 +285,7 @@ void ItemList::unselect_all() { items[i].selected = false; } - + current = -1; update(); } 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/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 f5890fa2ee..ebec61ee6d 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -430,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(); @@ -466,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) { @@ -489,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)); } @@ -1073,11 +1095,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(); } @@ -1310,7 +1336,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); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 44f02a9d3d..8ec51c7d3a 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -180,7 +180,7 @@ public: void remove_item(int p_idx); - void add_separator(); + void add_separator(const String &p_text = String()); void clear(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index ce2e3538da..857ae8ff4c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -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"); @@ -2245,6 +2248,21 @@ 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); @@ -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/text_edit.cpp b/scene/gui/text_edit.cpp index 218b5060a1..90cb475a7b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -290,6 +290,7 @@ 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; @@ -972,7 +973,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); } } @@ -4314,6 +4315,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"); @@ -4885,6 +4887,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); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 586f4c8e93..34d69bb508 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; @@ -106,6 +107,8 @@ public: 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; } bool is_hidden(int p_line) const { return text[p_line].hidden; } + void set_safe(int p_line, bool p_safe) { text[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/main/node.cpp b/scene/main/node.cpp index 6d18cce21d..e1333bcae2 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() { void Node::_propagate_exit_tree() { -//block while removing children + //block while removing children #ifdef DEBUG_ENABLED diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 01302d4214..9013d276c7 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -41,7 +41,7 @@ #include "scene/3d/spatial.h" #include "scene/gui/control.h" #include "scene/gui/label.h" -#include "scene/gui/panel.h" +#include "scene/gui/panel_container.h" #include "scene/main/timer.h" #include "scene/resources/mesh.h" #include "scene/scene_string_names.h" @@ -166,9 +166,9 @@ ViewportTexture::~ViewportTexture() { ///////////////////////////////////// -class TooltipPanel : public Panel { +class TooltipPanel : public PanelContainer { - GDCLASS(TooltipPanel, Panel) + GDCLASS(TooltipPanel, PanelContainer) public: TooltipPanel(){}; }; @@ -1305,10 +1305,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 +1318,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 +1343,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(); - - Ref<StyleBox> ttp = gui.tooltip_label->get_stylebox("panel", "TooltipPanel"); + //gui.tooltip_popup->hide(); - 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) @@ -1891,13 +1904,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 a0bac09442..1f1a7a8027 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -159,6 +159,8 @@ #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" @@ -310,21 +312,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>(); @@ -341,9 +341,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 @@ -456,6 +460,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>(); @@ -568,6 +605,7 @@ 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_class<Animation>(); ClassDB::register_virtual_class<Font>(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index e57a2b68c8..8acfdc482a 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -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; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 702953fa40..fe12e2f5f6 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)); 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/material.cpp b/scene/resources/material.cpp index 654d7b884e..56b3ee70bb 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"; } @@ -1855,6 +1869,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); @@ -2046,6 +2061,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); diff --git a/scene/resources/material.h b/scene/resources/material.h index 87594213bc..84f1005b03 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; @@ -190,6 +192,7 @@ public: FLAG_ALBEDO_TEXTURE_FORCE_SRGB, FLAG_DONT_RECEIVE_SHADOWS, FLAG_ENSURE_CORRECT_NORMALS, + FLAG_DISABLE_AMBIENT_LIGHT, FLAG_MAX }; @@ -238,7 +241,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 : 17; uint64_t detail_blend_mode : 2; uint64_t diffuse_mode : 3; uint64_t specular_mode : 2; diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 597866eb74..9df117d09c 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); } 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/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/texture.cpp b/scene/resources/texture.cpp index 54f5aea160..2baad555c0 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1669,3 +1669,208 @@ 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); +} diff --git a/scene/resources/texture.h b/scene/resources/texture.h index d81fd3b19b..c994bdad5f 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> */ @@ -521,4 +522,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/visual_shader.cpp b/scene/resources/visual_shader.cpp new file mode 100644 index 0000000000..b7b7802d1b --- /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_position", "type", "id"), &VisualShader::get_node_position); + ClassDB::bind_method(D_METHOD("get_node", "type"), &VisualShader::get_node); + + 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 |