summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp29
-rw-r--r--scene/2d/area_2d.cpp4
-rw-r--r--scene/2d/canvas_item.cpp38
-rw-r--r--scene/2d/canvas_item.h4
-rw-r--r--scene/2d/line_2d.cpp4
-rw-r--r--scene/2d/navigation2d.cpp4
-rw-r--r--scene/2d/navigation2d.h8
-rw-r--r--scene/2d/navigation_polygon.cpp6
-rw-r--r--scene/2d/particles_2d.cpp2
-rw-r--r--scene/2d/path_2d.cpp75
-rw-r--r--scene/2d/path_2d.h5
-rw-r--r--scene/2d/physics_body_2d.cpp16
-rw-r--r--scene/2d/tile_map.cpp54
-rw-r--r--scene/2d/tile_map.h20
-rw-r--r--scene/3d/area.cpp4
-rw-r--r--scene/3d/baked_lightmap.cpp718
-rw-r--r--scene/3d/baked_lightmap.h189
-rw-r--r--scene/3d/camera.cpp207
-rw-r--r--scene/3d/camera.h13
-rw-r--r--scene/3d/gi_probe.cpp1035
-rw-r--r--scene/3d/gi_probe.h80
-rw-r--r--scene/3d/light.cpp25
-rw-r--r--scene/3d/light.h15
-rw-r--r--scene/3d/navigation.cpp4
-rw-r--r--scene/3d/navigation.h8
-rw-r--r--scene/3d/navigation_mesh.cpp6
-rw-r--r--scene/3d/particles.cpp74
-rw-r--r--scene/3d/particles.h2
-rw-r--r--scene/3d/physics_body.cpp88
-rw-r--r--scene/3d/physics_body.h19
-rw-r--r--scene/3d/spatial.cpp4
-rw-r--r--scene/3d/vehicle_body.cpp20
-rw-r--r--scene/3d/voxel_light_baker.cpp2373
-rw-r--r--scene/3d/voxel_light_baker.h148
-rw-r--r--scene/animation/animation_player.cpp23
-rw-r--r--scene/animation/animation_tree_player.cpp6
-rw-r--r--scene/animation/tween_interpolaters.cpp22
-rw-r--r--scene/gui/box_container.h8
-rw-r--r--scene/gui/button.cpp26
-rw-r--r--scene/gui/button.h2
-rw-r--r--scene/gui/check_box.cpp47
-rw-r--r--scene/gui/check_box.h2
-rw-r--r--scene/gui/check_button.cpp36
-rw-r--r--scene/gui/check_button.h1
-rw-r--r--scene/gui/color_picker.cpp9
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/control.cpp11
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/dialogs.cpp13
-rw-r--r--scene/gui/file_dialog.cpp28
-rw-r--r--scene/gui/file_dialog.h5
-rw-r--r--scene/gui/line_edit.cpp4
-rw-r--r--scene/gui/menu_button.cpp1
-rw-r--r--scene/gui/option_button.cpp1
-rw-r--r--scene/gui/popup_menu.cpp138
-rw-r--r--scene/gui/popup_menu.h17
-rw-r--r--scene/gui/rich_text_label.cpp26
-rw-r--r--scene/gui/rich_text_label.h4
-rw-r--r--scene/gui/scroll_bar.h8
-rw-r--r--scene/gui/scroll_container.cpp2
-rw-r--r--scene/gui/slider.cpp17
-rw-r--r--scene/gui/slider.h8
-rw-r--r--scene/gui/split_container.h8
-rw-r--r--scene/gui/tab_container.cpp11
-rw-r--r--scene/gui/tab_container.h1
-rw-r--r--scene/gui/text_edit.cpp218
-rw-r--r--scene/gui/text_edit.h11
-rw-r--r--scene/gui/tree.cpp14
-rw-r--r--scene/main/http_request.cpp72
-rw-r--r--scene/main/http_request.h1
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/main/scene_tree.h4
-rw-r--r--scene/main/viewport.cpp26
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/animation.cpp38
-rw-r--r--scene/resources/animation.h7
-rw-r--r--scene/resources/box_shape.cpp4
-rw-r--r--scene/resources/capsule_shape.cpp4
-rw-r--r--scene/resources/capsule_shape_2d.cpp4
-rw-r--r--scene/resources/circle_shape_2d.cpp4
-rw-r--r--scene/resources/concave_polygon_shape.cpp4
-rw-r--r--scene/resources/concave_polygon_shape_2d.cpp4
-rw-r--r--scene/resources/convex_polygon_shape.cpp4
-rw-r--r--scene/resources/convex_polygon_shape_2d.cpp4
-rw-r--r--scene/resources/default_theme/default_theme.cpp14
-rw-r--r--scene/resources/default_theme/theme_data.h4
-rw-r--r--scene/resources/default_theme/tree_bg_disabled.pngbin0 -> 406 bytes
-rw-r--r--scene/resources/material.cpp35
-rw-r--r--scene/resources/material.h9
-rw-r--r--scene/resources/mesh.cpp213
-rw-r--r--scene/resources/mesh.h7
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/plane_shape.cpp4
-rw-r--r--scene/resources/primitive_meshes.cpp2
-rw-r--r--scene/resources/ray_shape.cpp4
-rw-r--r--scene/resources/rectangle_shape_2d.cpp4
-rw-r--r--scene/resources/scene_format_text.cpp774
-rw-r--r--scene/resources/scene_format_text.h26
-rw-r--r--scene/resources/segment_shape_2d.cpp8
-rw-r--r--scene/resources/shape_line_2d.cpp4
-rw-r--r--scene/resources/sky_box.cpp4
-rw-r--r--scene/resources/sphere_shape.cpp4
-rw-r--r--scene/resources/surface_tool.cpp114
-rw-r--r--scene/resources/surface_tool.h3
-rw-r--r--scene/resources/tile_set.cpp20
-rw-r--r--scene/resources/tile_set.h12
107 files changed, 5527 insertions, 1962 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 4865858b7d..de28fef929 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -355,38 +355,21 @@ void AnimatedSprite::_notification(int p_what) {
case NOTIFICATION_DRAW: {
- if (frames.is_null()) {
- print_line("no draw no faemos");
+ if (frames.is_null())
return;
- }
-
- if (frame < 0) {
- print_line("no draw frame <0");
+ if (frame < 0)
return;
- }
-
- if (!frames->has_animation(animation)) {
- print_line("no draw no anim: " + String(animation));
+ if (!frames->has_animation(animation))
return;
- }
Ref<Texture> texture = frames->get_frame(animation, frame);
- if (texture.is_null()) {
- print_line("no draw texture is null");
+ if (texture.is_null())
return;
- }
Ref<Texture> normal = frames->get_normal_frame(animation, frame);
- //print_line("DECIDED TO DRAW");
-
RID ci = get_canvas_item();
- /*
- texture->draw(ci,Point2());
- break;
- */
-
Size2i s;
s = texture->get_size();
Point2 ofs = offset;
@@ -403,9 +386,7 @@ void AnimatedSprite::_notification(int p_what) {
if (vflip)
dst_rect.size.y = -dst_rect.size.y;
- //texture->draw_rect(ci,dst_rect,false,modulate);
texture->draw_rect_region(ci, dst_rect, Rect2(Vector2(), texture->get_size()), Color(1, 1, 1), false, normal);
- //VisualServer::get_singleton()->canvas_item_add_texture_rect_region(ci,dst_rect,texture->get_rid(),src_rect,modulate);
} break;
}
@@ -568,7 +549,7 @@ void AnimatedSprite::stop() {
bool AnimatedSprite::is_playing() const {
- return is_processing();
+ return playing;
}
void AnimatedSprite::_reset_timeout() {
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 9ee77a710c..80f9bf0f9f 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -688,8 +688,8 @@ void Area2D::_bind_methods() {
BIND_ENUM_CONSTANT(SPACE_OVERRIDE_REPLACE_COMBINE);
}
-Area2D::Area2D()
- : CollisionObject2D(Physics2DServer::get_singleton()->area_create(), true) {
+Area2D::Area2D() :
+ CollisionObject2D(Physics2DServer::get_singleton()->area_create(), true) {
space_override = SPACE_OVERRIDE_DISABLED;
set_gravity(98);
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 66abe1baa8..82123d12ac 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -184,6 +184,11 @@ RID CanvasItemMaterial::get_shader_rid() const {
return shader_map[current_key].shader;
}
+Shader::Mode CanvasItemMaterial::get_shader_mode() const {
+
+ return Shader::MODE_CANVAS_ITEM;
+}
+
void CanvasItemMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);
@@ -206,8 +211,8 @@ void CanvasItemMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
}
-CanvasItemMaterial::CanvasItemMaterial()
- : element(this) {
+CanvasItemMaterial::CanvasItemMaterial() :
+ element(this) {
blend_mode = BLEND_MODE_MIX;
light_mode = LIGHT_MODE_NORMAL;
@@ -618,6 +623,29 @@ void CanvasItem::draw_polyline_colors(const Vector<Point2> &p_points, const Vect
VisualServer::get_singleton()->canvas_item_add_polyline(canvas_item, p_points, p_colors, p_width, p_antialiased);
}
+
+void CanvasItem::draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width, bool p_antialiased) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ Vector<Color> colors;
+ colors.push_back(p_color);
+ VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, colors, p_width, p_antialiased);
+}
+
+void CanvasItem::draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width, bool p_antialiased) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_multiline(canvas_item, p_points, p_colors, p_width, p_antialiased);
+}
+
void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
if (!drawing) {
@@ -974,6 +1002,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline", "points", "color", "width", "antialiased"), &CanvasItem::draw_polyline, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_polyline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_polyline_colors, DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_multiline", "points", "color", "width", "antialiased"), &CanvasItem::draw_multiline, DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("draw_multiline_colors", "points", "colors", "width", "antialiased"), &CanvasItem::draw_multiline_colors, DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture", "position", "modulate", "normal_map"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));
@@ -1126,8 +1156,8 @@ Rect2 CanvasItem::_edit_get_item_and_children_rect() const {
return rect;
}
-CanvasItem::CanvasItem()
- : xform_change(this) {
+CanvasItem::CanvasItem() :
+ xform_change(this) {
canvas_item = VisualServer::get_singleton()->canvas_item_create();
visible = true;
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index c877a94755..2384c0f370 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -123,6 +123,8 @@ public:
RID get_shader_rid() const;
+ virtual Shader::Mode get_shader_mode() const;
+
CanvasItemMaterial();
virtual ~CanvasItemMaterial();
};
@@ -266,6 +268,8 @@ public:
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
+ void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
+ void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, float p_width = 1.0, bool p_antialiased = false);
void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 9131223ff3..57a0e15447 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -37,8 +37,8 @@ VARIANT_ENUM_CAST(Line2D::LineJointMode)
VARIANT_ENUM_CAST(Line2D::LineCapMode)
VARIANT_ENUM_CAST(Line2D::LineTextureMode)
-Line2D::Line2D()
- : Node2D() {
+Line2D::Line2D() :
+ Node2D() {
_joint_mode = LINE_JOINT_SHARP;
_begin_cap_mode = LINE_CAP_NONE;
_end_cap_mode = LINE_CAP_NONE;
diff --git a/scene/2d/navigation2d.cpp b/scene/2d/navigation2d.cpp
index 9eff107827..40013814f8 100644
--- a/scene/2d/navigation2d.cpp
+++ b/scene/2d/navigation2d.cpp
@@ -205,7 +205,7 @@ void Navigation2D::_navpoly_unlink(int p_id) {
nm.linked = false;
}
-int Navigation2D::navpoly_create(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) {
+int Navigation2D::navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner) {
int id = last_id++;
NavMesh nm;
@@ -708,7 +708,7 @@ Object *Navigation2D::get_closest_point_owner(const Vector2 &p_point) {
void Navigation2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("navpoly_create", "mesh", "xform", "owner"), &Navigation2D::navpoly_create, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("navpoly_add", "mesh", "xform", "owner"), &Navigation2D::navpoly_add, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("navpoly_set_transform", "id", "xform"), &Navigation2D::navpoly_set_transform);
ClassDB::bind_method(D_METHOD("navpoly_remove", "id"), &Navigation2D::navpoly_remove);
diff --git a/scene/2d/navigation2d.h b/scene/2d/navigation2d.h
index e87b01f7c5..02dbcb0f96 100644
--- a/scene/2d/navigation2d.h
+++ b/scene/2d/navigation2d.h
@@ -57,9 +57,9 @@ class Navigation2D : public Node2D {
return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
};
- EdgeKey(const Point &p_a = Point(), const Point &p_b = Point())
- : a(p_a),
- b(p_b) {
+ EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
+ a(p_a),
+ b(p_b) {
if (a.key > b.key) {
SWAP(a, b);
}
@@ -159,7 +159,7 @@ protected:
public:
//API should be as dynamic as possible
- int navpoly_create(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL);
+ int navpoly_add(const Ref<NavigationPolygon> &p_mesh, const Transform2D &p_xform, Object *p_owner = NULL);
void navpoly_set_transform(int p_id, const Transform2D &p_xform);
void navpoly_remove(int p_id);
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index c53241e985..5a6a5128e6 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -293,7 +293,7 @@ void NavigationPolygonInstance::set_enabled(bool p_enabled) {
if (navpoly.is_valid()) {
- nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this);
+ nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
}
}
}
@@ -324,7 +324,7 @@ void NavigationPolygonInstance::_notification(int p_what) {
if (enabled && navpoly.is_valid()) {
- nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this);
+ nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
}
break;
}
@@ -419,7 +419,7 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg
}
if (navigation && navpoly.is_valid() && enabled) {
- nav_id = navigation->navpoly_create(navpoly, get_relative_transform_to_parent(navigation), this);
+ nav_id = navigation->navpoly_add(navpoly, get_relative_transform_to_parent(navigation), this);
}
//update_gizmo();
_change_notify("navpoly");
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index aee5d89150..7d53557216 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -291,7 +291,7 @@ void Particles2D::_notification(int p_what) {
texture_rid = texture->get_rid();
RID normal_rid;
if (normal_map.is_valid())
- normal_rid = texture->get_rid();
+ normal_rid = normal_map->get_rid();
VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames);
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index e1c7331393..4029ef137b 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -107,34 +107,56 @@ void PathFollow2D::_update_transform() {
if (!c.is_valid())
return;
- if (delta_offset == 0) {
- return;
- }
-
- float o = offset;
+ float path_length = c->get_baked_length();
+ float bounded_offset = offset;
if (loop)
- o = Math::fposmod(o, c->get_baked_length());
-
- Vector2 pos = c->interpolate_baked(o, cubic);
+ bounded_offset = Math::fposmod(bounded_offset, path_length);
+ else
+ bounded_offset = CLAMP(bounded_offset, 0, path_length);
- Vector2 displacement_offset = Vector2(h_offset, v_offset);
+ Vector2 pos = c->interpolate_baked(bounded_offset, cubic);
if (rotate) {
+ float ahead = bounded_offset + lookahead;
+
+ if (loop && ahead >= path_length) {
+ // If our lookahead will loop, we need to check if the path is closed.
+ int point_count = c->get_point_count();
+ if (point_count > 0) {
+ Vector2 start_point = c->get_point_position(0);
+ Vector2 end_point = c->get_point_position(point_count - 1);
+ if (start_point == end_point) {
+ // Since the path is closed we want to 'smooth off'
+ // the corner at the start/end.
+ // So we wrap the lookahead back round.
+ ahead = Math::fmod(ahead, path_length);
+ }
+ }
+ }
+
+ Vector2 ahead_pos = c->interpolate_baked(ahead, cubic);
- Vector2 t_prev = (pos - c->interpolate_baked(o - delta_offset, cubic)).normalized();
- Vector2 t_next = (c->interpolate_baked(o + delta_offset, cubic) - pos).normalized();
+ Vector2 tangent_to_curve;
+ if (ahead_pos == pos) {
+ // This will happen at the end of non-looping or non-closed paths.
+ // We'll try a look behind instead, in order to get a meaningful angle.
+ tangent_to_curve =
+ (pos - c->interpolate_baked(bounded_offset - lookahead, cubic)).normalized();
+ } else {
+ tangent_to_curve = (ahead_pos - pos).normalized();
+ }
- float angle = t_prev.angle_to(t_next);
+ Vector2 normal_of_curve = -tangent_to_curve.tangent();
- set_rotation(get_rotation() + angle);
+ pos += tangent_to_curve * h_offset;
+ pos += normal_of_curve * v_offset;
- Vector2 n = t_next;
- Vector2 t = -n.tangent();
- pos += n * h_offset + t * v_offset;
+ set_rotation(tangent_to_curve.angle());
} else {
- pos += displacement_offset;
+ pos.x += h_offset;
+ pos.y += v_offset;
}
set_position(pos);
@@ -185,6 +207,8 @@ bool PathFollow2D::_set(const StringName &p_name, const Variant &p_value) {
set_cubic_interpolation(p_value);
} else if (String(p_name) == "loop") {
set_loop(p_value);
+ } else if (String(p_name) == "lookahead") {
+ set_lookahead(p_value);
} else
return false;
@@ -207,6 +231,8 @@ bool PathFollow2D::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = cubic;
} else if (String(p_name) == "loop") {
r_ret = loop;
+ } else if (String(p_name) == "lookahead") {
+ r_ret = lookahead;
} else
return false;
@@ -224,6 +250,7 @@ void PathFollow2D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::BOOL, "rotate"));
p_list->push_back(PropertyInfo(Variant::BOOL, "cubic_interp"));
p_list->push_back(PropertyInfo(Variant::BOOL, "loop"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001"));
}
String PathFollow2D::get_configuration_warning() const {
@@ -263,7 +290,7 @@ void PathFollow2D::_bind_methods() {
}
void PathFollow2D::set_offset(float p_offset) {
- delta_offset = p_offset - offset;
+
offset = p_offset;
if (path)
_update_transform();
@@ -314,6 +341,16 @@ float PathFollow2D::get_unit_offset() const {
return 0;
}
+void PathFollow2D::set_lookahead(float p_lookahead) {
+
+ lookahead = p_lookahead;
+}
+
+float PathFollow2D::get_lookahead() const {
+
+ return lookahead;
+}
+
void PathFollow2D::set_rotate(bool p_rotate) {
rotate = p_rotate;
@@ -338,11 +375,11 @@ bool PathFollow2D::has_loop() const {
PathFollow2D::PathFollow2D() {
offset = 0;
- delta_offset = 0;
h_offset = 0;
v_offset = 0;
path = NULL;
rotate = true;
cubic = true;
loop = true;
+ lookahead = 4;
}
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index f5ba3a3d32..88a0abdea9 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -60,9 +60,9 @@ public:
private:
Path2D *path;
real_t offset;
- real_t delta_offset; // change in offset since last _update_transform
real_t h_offset;
real_t v_offset;
+ real_t lookahead;
bool cubic;
bool loop;
bool rotate;
@@ -90,6 +90,9 @@ public:
void set_unit_offset(float p_unit_offset);
float get_unit_offset() const;
+ void set_lookahead(float p_lookahead);
+ float get_lookahead() const;
+
void set_loop(bool p_loop);
bool has_loop() const;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 1f6127e6eb..a1a1101b11 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -132,8 +132,8 @@ bool PhysicsBody2D::get_collision_layer_bit(int p_bit) const {
return get_collision_layer() & (1 << p_bit);
}
-PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode)
- : CollisionObject2D(Physics2DServer::get_singleton()->body_create(), false) {
+PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) :
+ CollisionObject2D(Physics2DServer::get_singleton()->body_create(), false) {
Physics2DServer::get_singleton()->body_set_mode(get_rid(), p_mode);
collision_layer = 1;
@@ -226,8 +226,8 @@ void StaticBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
}
-StaticBody2D::StaticBody2D()
- : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) {
+StaticBody2D::StaticBody2D() :
+ PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) {
constant_angular_velocity = 0;
bounce = 0;
@@ -921,8 +921,8 @@ void RigidBody2D::_bind_methods() {
BIND_ENUM_CONSTANT(CCD_MODE_CAST_SHAPE);
}
-RigidBody2D::RigidBody2D()
- : PhysicsBody2D(Physics2DServer::BODY_MODE_RIGID) {
+RigidBody2D::RigidBody2D() :
+ PhysicsBody2D(Physics2DServer::BODY_MODE_RIGID) {
mode = MODE_RIGID;
@@ -1144,8 +1144,8 @@ void KinematicBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
-KinematicBody2D::KinematicBody2D()
- : PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC) {
+KinematicBody2D::KinematicBody2D() :
+ PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC) {
margin = 0.08;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 69104ea4b2..7b30ddaa56 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "tile_map.h"
+
#include "io/marshalls.h"
#include "method_bind_ext.gen.inc"
#include "os/os.h"
@@ -169,10 +170,12 @@ void TileMap::set_cell_size(Size2 p_size) {
_recreate_quadrants();
emit_signal("settings_changed");
}
+
Size2 TileMap::get_cell_size() const {
return cell_size;
}
+
void TileMap::set_quadrant_size(int p_size) {
ERR_FAIL_COND(p_size < 1);
@@ -182,32 +185,12 @@ void TileMap::set_quadrant_size(int p_size) {
_recreate_quadrants();
emit_signal("settings_changed");
}
+
int TileMap::get_quadrant_size() const {
return quadrant_size;
}
-void TileMap::set_center_x(bool p_enable) {
-
- center_x = p_enable;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-bool TileMap::get_center_x() const {
-
- return center_x;
-}
-void TileMap::set_center_y(bool p_enable) {
-
- center_y = p_enable;
- _recreate_quadrants();
- emit_signal("settings_changed");
-}
-bool TileMap::get_center_y() const {
-
- return center_y;
-}
-
void TileMap::_fix_cell_transform(Transform2D &xform, const Cell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) {
Size2 s = p_sc;
@@ -503,7 +486,7 @@ void TileMap::_update_dirty_quadrants() {
xform.set_origin(offset.floor() + q.pos);
_fix_cell_transform(xform, c, npoly_ofs + center_ofs, s);
- int pid = navigation->navpoly_create(navpoly, nav_rel * xform);
+ int pid = navigation->navpoly_add(navpoly, nav_rel * xform);
Quadrant::NavPoly np;
np.id = pid;
@@ -513,16 +496,13 @@ void TileMap::_update_dirty_quadrants() {
}
Ref<OccluderPolygon2D> occluder;
- Vector2 occluder_ofs;
if (tile_set->tile_get_is_autotile(c.id)) {
occluder = tile_set->autotile_get_light_occluder(c.id, Vector2(c.autotile_coord_x, c.autotile_coord_y));
- occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
} else {
occluder = tile_set->tile_get_light_occluder(c.id);
- occluder_ofs = Vector2();
}
if (occluder.is_valid()) {
-
+ Vector2 occluder_ofs = tile_set->tile_get_occluder_offset(c.id);
Transform2D xform;
xform.set_origin(offset.floor() + q.pos);
_fix_cell_transform(xform, c, occluder_ofs + center_ofs, s);
@@ -988,14 +968,14 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
int c = p_data.size();
PoolVector<int>::Read r = p_data.read();
- int offset = (format == FORMAT_2_1_5) ? 3 : 2;
+ int offset = (format == FORMAT_2) ? 3 : 2;
clear();
for (int i = 0; i < c; i += offset) {
const uint8_t *ptr = (const uint8_t *)&r[i];
uint8_t local[12];
- for (int j = 0; j < ((format == FORMAT_2_1_5) ? 12 : 8); j++)
+ for (int j = 0; j < ((format == FORMAT_2) ? 12 : 8); j++)
local[j] = ptr[j];
#ifdef BIG_ENDIAN_ENABLED
@@ -1005,7 +985,7 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
SWAP(local[4], local[7]);
SWAP(local[5], local[6]);
//TODO: ask someone to check this...
- if (FORMAT == FORMAT_2_1_5) {
+ if (FORMAT == FORMAT_2) {
SWAP(local[8], local[11]);
SWAP(local[9], local[10]);
}
@@ -1020,7 +1000,7 @@ void TileMap::_set_tile_data(const PoolVector<int> &p_data) {
v &= (1 << 29) - 1;
int16_t coord_x;
int16_t coord_y;
- if (format == FORMAT_2_1_5) {
+ if (format == FORMAT_2) {
coord_x = decode_uint16(&local[8]);
coord_y = decode_uint16(&local[10]);
}
@@ -1038,6 +1018,8 @@ PoolVector<int> TileMap::_get_tile_data() const {
data.resize(tile_map.size() * 3);
PoolVector<int>::Write w = data.write();
+ format = FORMAT_2;
+
int idx = 0;
for (const Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) {
uint8_t *ptr = (uint8_t *)&w[idx];
@@ -1318,7 +1300,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
if (p_name == "format") {
- r_ret = FORMAT_2_1_5;
+ r_ret = FORMAT_2;
return true;
} else if (p_name == "tile_data") {
r_ret = _get_tile_data();
@@ -1494,12 +1476,6 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tile_origin", "origin"), &TileMap::set_tile_origin);
ClassDB::bind_method(D_METHOD("get_tile_origin"), &TileMap::get_tile_origin);
- ClassDB::bind_method(D_METHOD("set_center_x", "enable"), &TileMap::set_center_x);
- ClassDB::bind_method(D_METHOD("get_center_x"), &TileMap::get_center_x);
-
- ClassDB::bind_method(D_METHOD("set_center_y", "enable"), &TileMap::set_center_y);
- ClassDB::bind_method(D_METHOD("get_center_y"), &TileMap::get_center_y);
-
ClassDB::bind_method(D_METHOD("set_clip_uv", "enable"), &TileMap::set_clip_uv);
ClassDB::bind_method(D_METHOD("get_clip_uv"), &TileMap::get_clip_uv);
@@ -1604,8 +1580,6 @@ TileMap::TileMap() {
quadrant_order_dirty = false;
quadrant_size = 16;
cell_size = Size2(64, 64);
- center_x = false;
- center_y = false;
collision_layer = 1;
collision_mask = 1;
friction = 1;
@@ -1617,7 +1591,7 @@ TileMap::TileMap() {
y_sort_mode = false;
occluder_light_mask = 1;
clip_uv = false;
- format = FORMAT_2_1_4; //Always initialize with the lowest format
+ format = FORMAT_1; //Always initialize with the lowest format
fp_adjust = 0.00001;
tile_origin = TILE_ORIGIN_TOP_LEFT;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index b7f1a57023..8f0b2e6e4c 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -61,14 +61,13 @@ public:
private:
enum DataFormat {
- FORMAT_2_1_4 = 0,
- FORMAT_2_1_5
+ FORMAT_1 = 0,
+ FORMAT_2
};
Ref<TileSet> tile_set;
Size2i cell_size;
int quadrant_size;
- bool center_x, center_y;
Mode mode;
Transform2D custom_transform;
HalfOffset half_offset;
@@ -147,8 +146,8 @@ private:
navpoly_ids = q.navpoly_ids;
occluder_instances = q.occluder_instances;
}
- Quadrant(const Quadrant &q)
- : dirty_list(this) {
+ Quadrant(const Quadrant &q) :
+ dirty_list(this) {
pos = q.pos;
canvas_items = q.canvas_items;
body = q.body;
@@ -156,8 +155,8 @@ private:
occluder_instances = q.occluder_instances;
navpoly_ids = q.navpoly_ids;
}
- Quadrant()
- : dirty_list(this) {}
+ Quadrant() :
+ dirty_list(this) {}
};
Map<PosKey, Quadrant> quadrant_map;
@@ -178,7 +177,7 @@ private:
float bounce;
uint32_t collision_layer;
uint32_t collision_mask;
- DataFormat format;
+ mutable DataFormat format;
TileOrigin tile_origin;
@@ -231,11 +230,6 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
- void set_center_x(bool p_enable);
- bool get_center_x() const;
- void set_center_y(bool p_enable);
- bool get_center_y() const;
-
void set_cell(int p_x, int p_y, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false, Vector2 p_autotile_coord = Vector2());
int get_cell(int p_x, int p_y) const;
bool is_cell_x_flipped(int p_x, int p_y) const;
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 266bc5e381..422aa556f9 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -737,8 +737,8 @@ void Area::_bind_methods() {
BIND_ENUM_CONSTANT(SPACE_OVERRIDE_REPLACE_COMBINE);
}
-Area::Area()
- : CollisionObject(PhysicsServer::get_singleton()->area_create(), true) {
+Area::Area() :
+ CollisionObject(PhysicsServer::get_singleton()->area_create(), true) {
space_override = SPACE_OVERRIDE_DISABLED;
set_gravity(9.8);
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
new file mode 100644
index 0000000000..3d9bb73181
--- /dev/null
+++ b/scene/3d/baked_lightmap.cpp
@@ -0,0 +1,718 @@
+#include "baked_lightmap.h"
+#include "io/resource_saver.h"
+#include "os/dir_access.h"
+#include "os/os.h"
+#include "voxel_light_baker.h"
+
+void BakedLightmapData::set_bounds(const AABB &p_bounds) {
+
+ bounds = p_bounds;
+ VS::get_singleton()->lightmap_capture_set_bounds(baked_light, p_bounds);
+}
+
+AABB BakedLightmapData::get_bounds() const {
+
+ return bounds;
+}
+
+void BakedLightmapData::set_octree(const PoolVector<uint8_t> &p_octree) {
+
+ VS::get_singleton()->lightmap_capture_set_octree(baked_light, p_octree);
+}
+
+PoolVector<uint8_t> BakedLightmapData::get_octree() const {
+
+ return VS::get_singleton()->lightmap_capture_get_octree(baked_light);
+}
+
+void BakedLightmapData::set_cell_space_transform(const Transform &p_xform) {
+
+ cell_space_xform = p_xform;
+ VS::get_singleton()->lightmap_capture_set_octree_cell_transform(baked_light, p_xform);
+}
+
+Transform BakedLightmapData::get_cell_space_transform() const {
+ return cell_space_xform;
+}
+
+void BakedLightmapData::set_cell_subdiv(int p_cell_subdiv) {
+ cell_subdiv = p_cell_subdiv;
+ VS::get_singleton()->lightmap_capture_set_octree_cell_subdiv(baked_light, p_cell_subdiv);
+}
+
+int BakedLightmapData::get_cell_subdiv() const {
+ return cell_subdiv;
+}
+
+void BakedLightmapData::set_energy(float p_energy) {
+
+ energy = p_energy;
+ VS::get_singleton()->lightmap_capture_set_energy(baked_light, energy);
+}
+
+float BakedLightmapData::get_energy() const {
+
+ return energy;
+}
+
+void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap) {
+
+ ERR_FAIL_COND(p_lightmap.is_null());
+ User user;
+ user.path = p_path;
+ user.lightmap = p_lightmap;
+ users.push_back(user);
+}
+
+int BakedLightmapData::get_user_count() const {
+
+ return users.size();
+}
+NodePath BakedLightmapData::get_user_path(int p_user) const {
+
+ ERR_FAIL_INDEX_V(p_user, users.size(), NodePath());
+ return users[p_user].path;
+}
+Ref<Texture> BakedLightmapData::get_user_lightmap(int p_user) const {
+
+ ERR_FAIL_INDEX_V(p_user, users.size(), Ref<Texture>());
+ return users[p_user].lightmap;
+}
+
+void BakedLightmapData::clear_users() {
+ users.clear();
+}
+
+void BakedLightmapData::_set_user_data(const Array &p_data) {
+
+ ERR_FAIL_COND(p_data.size() & 1);
+
+ for (int i = 0; i < p_data.size(); i += 2) {
+ add_user(p_data[i], p_data[i + 1]);
+ }
+}
+
+Array BakedLightmapData::_get_user_data() const {
+
+ Array ret;
+ for (int i = 0; i < users.size(); i++) {
+ ret.push_back(users[i].path);
+ ret.push_back(users[i].lightmap);
+ }
+ return ret;
+}
+
+RID BakedLightmapData::get_rid() const {
+ return baked_light;
+}
+void BakedLightmapData::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &BakedLightmapData::_set_user_data);
+ ClassDB::bind_method(D_METHOD("_get_user_data"), &BakedLightmapData::_get_user_data);
+
+ ClassDB::bind_method(D_METHOD("set_bounds", "bounds"), &BakedLightmapData::set_bounds);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &BakedLightmapData::get_bounds);
+
+ ClassDB::bind_method(D_METHOD("set_cell_space_transform", "xform"), &BakedLightmapData::set_cell_space_transform);
+ ClassDB::bind_method(D_METHOD("get_cell_space_transform"), &BakedLightmapData::get_cell_space_transform);
+
+ ClassDB::bind_method(D_METHOD("set_cell_subdiv", "cell_subdiv"), &BakedLightmapData::set_cell_subdiv);
+ ClassDB::bind_method(D_METHOD("get_cell_subdiv"), &BakedLightmapData::get_cell_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_octree", "octree"), &BakedLightmapData::set_octree);
+ ClassDB::bind_method(D_METHOD("get_octree"), &BakedLightmapData::get_octree);
+
+ ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmapData::set_energy);
+ ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmapData::get_energy);
+
+ ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap"), &BakedLightmapData::add_user);
+ ClassDB::bind_method(D_METHOD("get_user_count"), &BakedLightmapData::get_user_count);
+ ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &BakedLightmapData::get_user_path);
+ ClassDB::bind_method(D_METHOD("get_user_lightmap", "user_idx"), &BakedLightmapData::get_user_lightmap);
+ ClassDB::bind_method(D_METHOD("clear_users"), &BakedLightmapData::clear_users);
+
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "bounds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_bounds", "get_bounds");
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "octree", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_octree", "get_octree");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "cell_space_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_space_transform", "get_cell_space_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_subdiv", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_cell_subdiv", "get_cell_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_user_data", "_get_user_data");
+}
+
+BakedLightmapData::BakedLightmapData() {
+
+ baked_light = VS::get_singleton()->lightmap_capture_create();
+ energy = 1;
+ cell_subdiv = 1;
+}
+
+BakedLightmapData::~BakedLightmapData() {
+
+ VS::get_singleton()->free(baked_light);
+}
+
+///////////////////////////
+
+BakedLightmap::BakeBeginFunc BakedLightmap::bake_begin_function = NULL;
+BakedLightmap::BakeStepFunc BakedLightmap::bake_step_function = NULL;
+BakedLightmap::BakeEndFunc BakedLightmap::bake_end_function = NULL;
+
+void BakedLightmap::set_bake_subdiv(Subdiv p_subdiv) {
+ bake_subdiv = p_subdiv;
+}
+
+BakedLightmap::Subdiv BakedLightmap::get_bake_subdiv() const {
+ return bake_subdiv;
+}
+
+void BakedLightmap::set_capture_subdiv(Subdiv p_subdiv) {
+ capture_subdiv = p_subdiv;
+}
+
+BakedLightmap::Subdiv BakedLightmap::get_capture_subdiv() const {
+ return capture_subdiv;
+}
+
+void BakedLightmap::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ update_gizmo();
+}
+
+Vector3 BakedLightmap::get_extents() const {
+ return extents;
+}
+
+void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights) {
+
+ MeshInstance *mi = Object::cast_to<MeshInstance>(p_at_node);
+ if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (mesh.is_valid()) {
+
+ bool all_have_uv2 = true;
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ if (!(mesh->surface_get_format(i) & Mesh::ARRAY_FORMAT_TEX_UV2)) {
+ all_have_uv2 = false;
+ break;
+ }
+ }
+
+ if (all_have_uv2 && mesh->get_lightmap_size_hint() != Size2()) {
+ //READY TO BAKE! size hint could be computed if not found, actually..
+
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
+
+ if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ pm.path = get_path_to(mi);
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ pm.instance_materials.push_back(mi->get_surface_material(i));
+ }
+ pm.override_material = mi->get_material_override();
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+ }
+
+ Light *light = Object::cast_to<Light>(p_at_node);
+
+ if (light && light->get_bake_mode() != Light::BAKE_DISABLED) {
+ PlotLight pl;
+ Transform xf = get_global_transform().affine_inverse() * light->get_global_transform();
+
+ pl.local_xform = xf;
+ pl.light = light;
+ plot_lights.push_back(pl);
+ }
+ for (int i = 0; i < p_at_node->get_child_count(); i++) {
+
+ Node *child = p_at_node->get_child(i);
+ if (!child->get_owner())
+ continue; //maybe a helper
+
+ _find_meshes_and_lights(child, plot_meshes, plot_lights);
+ }
+}
+
+void BakedLightmap::set_hdr(bool p_enable) {
+ hdr = p_enable;
+}
+
+bool BakedLightmap::is_hdr() const {
+ return hdr;
+}
+
+bool BakedLightmap::_bake_time(void *ud, float p_secs, float p_progress) {
+
+ uint64_t time = OS::get_singleton()->get_ticks_usec();
+ BakeTimeData *btd = (BakeTimeData *)ud;
+
+ if (time - btd->last_step > 1000000) {
+
+ int mins_left = p_secs / 60;
+ int secs_left = Math::fmod(p_secs, 60.0f);
+ int percent = p_progress * 100;
+ bool abort = bake_step_function(btd->pass + percent, btd->text + " " + itos(percent) + "% (Time Left: " + itos(mins_left) + ":" + itos(secs_left) + "s)");
+ btd->last_step = time;
+ if (abort)
+ return true;
+ }
+
+ return false;
+}
+
+BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_visual_debug) {
+
+ String save_path;
+
+ if (image_path.begins_with("res://")) {
+ save_path = image_path;
+ } else {
+ if (get_filename() != "") {
+ save_path = get_filename().get_base_dir();
+ } else if (get_owner() && get_owner()->get_filename() != "") {
+ save_path = get_owner()->get_filename().get_base_dir();
+ }
+
+ if (save_path == "") {
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
+ if (image_path != "") {
+ save_path.plus_file(image_path);
+ }
+ }
+ {
+ //check for valid save path
+ DirAccessRef d = DirAccess::open(save_path);
+ if (!d) {
+ ERR_PRINTS("Invalid Save Path: " + save_path);
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
+ }
+
+ Ref<BakedLightmapData> new_light_data;
+ new_light_data.instance();
+
+ static const int subdiv_value[SUBDIV_MAX] = { 8, 9, 10, 11, 12, 13 };
+
+ VoxelLightBaker baker;
+
+ baker.begin_bake(subdiv_value[bake_subdiv], AABB(-extents, extents * 2.0));
+
+ List<PlotMesh> mesh_list;
+ List<PlotLight> light_list;
+
+ _find_meshes_and_lights(p_from_node ? p_from_node : get_parent(), mesh_list, light_list);
+
+ if (bake_begin_function) {
+ bake_begin_function(mesh_list.size() + light_list.size() + 1 + mesh_list.size() * 100);
+ }
+
+ int step = 0;
+
+ int pmc = 0;
+
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
+
+ if (bake_step_function) {
+ bake_step_function(step++, RTR("Plotting Meshes: ") + " (" + itos(pmc + 1) + "/" + itos(mesh_list.size()) + ")");
+ }
+
+ pmc++;
+ baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material);
+ }
+
+ pmc = 0;
+ baker.begin_bake_light(VoxelLightBaker::BakeQuality(bake_quality), VoxelLightBaker::BakeMode(bake_mode), propagation, energy);
+
+ for (List<PlotLight>::Element *E = light_list.front(); E; E = E->next()) {
+
+ if (bake_step_function) {
+ bake_step_function(step++, RTR("Plotting Lights:") + " (" + itos(pmc + 1) + "/" + itos(light_list.size()) + ")");
+ }
+
+ pmc++;
+ PlotLight pl = E->get();
+ switch (pl.light->get_light_type()) {
+ case VS::LIGHT_DIRECTIONAL: {
+ baker.plot_light_directional(-pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_bake_mode() == Light::BAKE_ALL);
+ } break;
+ case VS::LIGHT_OMNI: {
+ baker.plot_light_omni(pl.local_xform.origin, pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
+ } break;
+ case VS::LIGHT_SPOT: {
+ baker.plot_light_spot(pl.local_xform.origin, pl.local_xform.basis.get_axis(2), pl.light->get_color(), pl.light->get_param(Light::PARAM_ENERGY), pl.light->get_param(Light::PARAM_INDIRECT_ENERGY), pl.light->get_param(Light::PARAM_RANGE), pl.light->get_param(Light::PARAM_ATTENUATION), pl.light->get_param(Light::PARAM_SPOT_ANGLE), pl.light->get_param(Light::PARAM_SPOT_ATTENUATION), pl.light->get_bake_mode() == Light::BAKE_ALL);
+
+ } break;
+ }
+ }
+ /*if (bake_step_function) {
+ bake_step_function(pmc++, RTR("Finishing Plot"));
+ }*/
+
+ baker.end_bake();
+
+ Set<String> used_mesh_names;
+
+ pmc = 0;
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
+
+ String mesh_name = E->get().mesh->get_name();
+ if (mesh_name == "" || mesh_name.find(":") != -1 || mesh_name.find("/") != -1) {
+ mesh_name = "LightMap";
+ }
+
+ if (used_mesh_names.has(mesh_name)) {
+ int idx = 2;
+ String base = mesh_name;
+ while (true) {
+ mesh_name = base + itos(idx);
+ if (!used_mesh_names.has(mesh_name))
+ break;
+ idx++;
+ }
+ }
+ used_mesh_names.insert(mesh_name);
+
+ pmc++;
+ VoxelLightBaker::LightMapData lm;
+
+ Error err;
+ if (bake_step_function) {
+ BakeTimeData btd;
+ btd.text = RTR("Lighting Meshes: ") + mesh_name + " (" + itos(pmc) + "/" + itos(mesh_list.size()) + ")";
+ btd.pass = step;
+ btd.last_step = 0;
+ err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm, _bake_time, &btd);
+ if (err != OK) {
+ bake_end_function();
+ if (err == ERR_SKIP)
+ return BAKE_ERROR_USER_ABORTED;
+ return BAKE_ERROR_CANT_CREATE_IMAGE;
+ }
+ step += 100;
+ } else {
+
+ err = baker.make_lightmap(E->get().local_xform, E->get().mesh, lm);
+ }
+
+ if (err == OK) {
+
+ Ref<Image> image;
+ image.instance();
+
+ uint32_t tex_flags = Texture::FLAGS_DEFAULT;
+ if (hdr) {
+
+ //just save a regular image
+ PoolVector<uint8_t> data;
+ int s = lm.light.size();
+ data.resize(lm.light.size() * 2);
+ {
+
+ PoolVector<uint8_t>::Write w = data.write();
+ PoolVector<float>::Read r = lm.light.read();
+ uint16_t *hfw = (uint16_t *)w.ptr();
+ for (int i = 0; i < s; i++) {
+ hfw[i] = Math::make_half_float(r[i]);
+ }
+ }
+
+ image->create(lm.width, lm.height, false, Image::FORMAT_RGBH, data);
+
+ } else {
+
+ //just save a regular image
+ PoolVector<uint8_t> data;
+ int s = lm.light.size();
+ data.resize(lm.light.size());
+ {
+
+ PoolVector<uint8_t>::Write w = data.write();
+ PoolVector<float>::Read r = lm.light.read();
+ for (int i = 0; i < s; i += 3) {
+ Color c(r[i + 0], r[i + 1], r[i + 2]);
+ c = c.to_srgb();
+ w[i + 0] = CLAMP(c.r * 255, 0, 255);
+ w[i + 1] = CLAMP(c.g * 255, 0, 255);
+ w[i + 2] = CLAMP(c.b * 255, 0, 255);
+ }
+ }
+
+ image->create(lm.width, lm.height, false, Image::FORMAT_RGB8, data);
+
+ //This texture is saved to SRGB for two reasons:
+ // 1) first is so it looks better when doing the LINEAR->SRGB conversion (more accurate)
+ // 2) So it can be used in the GLES2 backend, which does not support linkear workflow
+ tex_flags |= Texture::FLAG_CONVERT_TO_LINEAR;
+ }
+
+ Ref<ImageTexture> tex;
+ String image_path = save_path.plus_file(mesh_name + ".tex");
+ bool set_path = true;
+ if (ResourceCache::has(image_path)) {
+ tex = Ref<Resource>((Resource *)ResourceCache::get(image_path));
+ set_path = false;
+ }
+
+ if (!tex.is_valid()) {
+ tex.instance();
+ }
+
+ tex->create_from_image(image, tex_flags);
+
+ err = ResourceSaver::save(image_path, tex, ResourceSaver::FLAG_CHANGE_PATH);
+ if (err != OK) {
+ if (bake_end_function) {
+ bake_end_function();
+ }
+ ERR_FAIL_COND_V(err != OK, BAKE_ERROR_CANT_CREATE_IMAGE);
+ }
+
+ if (set_path) {
+ tex->set_path(image_path);
+ }
+ new_light_data->add_user(E->get().path, tex);
+ }
+ }
+
+ int csubdiv = subdiv_value[capture_subdiv];
+ AABB bounds = AABB(-extents, extents * 2);
+ new_light_data->set_cell_subdiv(csubdiv);
+ new_light_data->set_bounds(bounds);
+ new_light_data->set_octree(baker.create_capture_octree(csubdiv));
+ {
+
+ Transform to_bounds;
+ to_bounds.basis.scale(Vector3(bounds.get_longest_axis_size(), bounds.get_longest_axis_size(), bounds.get_longest_axis_size()));
+ to_bounds.origin = bounds.position;
+
+ Transform to_grid;
+ to_grid.basis.scale(Vector3(1 << (csubdiv - 1), 1 << (csubdiv - 1), 1 << (csubdiv - 1)));
+
+ Transform to_cell_space = to_grid * to_bounds.affine_inverse();
+ new_light_data->set_cell_space_transform(to_cell_space);
+ }
+
+ if (bake_end_function) {
+ bake_end_function();
+ }
+
+ //create the data for visual server
+
+ if (p_create_visual_debug) {
+ MultiMeshInstance *mmi = memnew(MultiMeshInstance);
+ mmi->set_multimesh(baker.create_debug_multimesh(VoxelLightBaker::DEBUG_LIGHT));
+ add_child(mmi);
+#ifdef TOOLS_ENABLED
+ if (get_tree()->get_edited_scene_root() == this) {
+ mmi->set_owner(this);
+ } else {
+ mmi->set_owner(get_owner());
+ }
+#else
+ mmi->set_owner(get_owner());
+#endif
+ }
+
+ set_light_data(new_light_data);
+
+ return BAKE_ERROR_OK;
+}
+
+void BakedLightmap::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+
+ if (light_data.is_valid()) {
+ _assign_lightmaps();
+ }
+ request_ready(); //will need ready again if re-enters tree
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+
+ if (light_data.is_valid()) {
+ _clear_lightmaps();
+ }
+ }
+}
+
+void BakedLightmap::_assign_lightmaps() {
+
+ ERR_FAIL_COND(!light_data.is_valid());
+
+ for (int i = 0; i < light_data->get_user_count(); i++) {
+ Node *node = get_node(light_data->get_user_path(i));
+ VisualInstance *vi = Object::cast_to<VisualInstance>(node);
+ ERR_CONTINUE(!vi);
+ Ref<Texture> lightmap = light_data->get_user_lightmap(i);
+ ERR_CONTINUE(!lightmap.is_valid());
+ VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), get_instance(), lightmap->get_rid());
+ }
+}
+
+void BakedLightmap::_clear_lightmaps() {
+ ERR_FAIL_COND(!light_data.is_valid());
+ for (int i = 0; i < light_data->get_user_count(); i++) {
+ Node *node = get_node(light_data->get_user_path(i));
+ VisualInstance *vi = Object::cast_to<VisualInstance>(node);
+ ERR_CONTINUE(!vi);
+ VS::get_singleton()->instance_set_use_lightmap(vi->get_instance(), RID(), RID());
+ }
+}
+
+void BakedLightmap::set_light_data(const Ref<BakedLightmapData> &p_data) {
+
+ if (light_data.is_valid()) {
+ if (is_inside_tree()) {
+ _clear_lightmaps();
+ }
+ set_base(RID());
+ }
+ light_data = p_data;
+
+ if (light_data.is_valid()) {
+ set_base(light_data->get_rid());
+ if (is_inside_tree()) {
+ _assign_lightmaps();
+ }
+ }
+}
+
+Ref<BakedLightmapData> BakedLightmap::get_light_data() const {
+
+ return light_data;
+}
+
+void BakedLightmap::_debug_bake() {
+ bake(get_parent(), true);
+}
+
+void BakedLightmap::set_propagation(float p_propagation) {
+ propagation = p_propagation;
+}
+
+float BakedLightmap::get_propagation() const {
+
+ return propagation;
+}
+
+void BakedLightmap::set_energy(float p_energy) {
+ energy = p_energy;
+}
+
+float BakedLightmap::get_energy() const {
+
+ return energy;
+}
+
+void BakedLightmap::set_bake_quality(BakeQuality p_quality) {
+ bake_quality = p_quality;
+}
+
+BakedLightmap::BakeQuality BakedLightmap::get_bake_quality() const {
+ return bake_quality;
+}
+
+void BakedLightmap::set_bake_mode(BakeMode p_mode) {
+ bake_mode = p_mode;
+}
+
+BakedLightmap::BakeMode BakedLightmap::get_bake_mode() const {
+ return bake_mode;
+}
+
+void BakedLightmap::set_image_path(const String &p_path) {
+ image_path = p_path;
+}
+
+String BakedLightmap::get_image_path() const {
+ return image_path;
+}
+
+AABB BakedLightmap::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+PoolVector<Face3> BakedLightmap::get_faces(uint32_t p_usage_flags) const {
+ return PoolVector<Face3>();
+}
+
+void BakedLightmap::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_light_data", "data"), &BakedLightmap::set_light_data);
+ ClassDB::bind_method(D_METHOD("get_light_data"), &BakedLightmap::get_light_data);
+
+ ClassDB::bind_method(D_METHOD("set_bake_subdiv", "bake_subdiv"), &BakedLightmap::set_bake_subdiv);
+ ClassDB::bind_method(D_METHOD("get_bake_subdiv"), &BakedLightmap::get_bake_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_capture_subdiv", "capture_subdiv"), &BakedLightmap::set_capture_subdiv);
+ ClassDB::bind_method(D_METHOD("get_capture_subdiv"), &BakedLightmap::get_capture_subdiv);
+
+ ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &BakedLightmap::set_bake_quality);
+ ClassDB::bind_method(D_METHOD("get_bake_quality"), &BakedLightmap::get_bake_quality);
+
+ ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &BakedLightmap::set_bake_mode);
+ ClassDB::bind_method(D_METHOD("get_bake_mode"), &BakedLightmap::get_bake_mode);
+
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &BakedLightmap::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &BakedLightmap::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &BakedLightmap::set_propagation);
+ ClassDB::bind_method(D_METHOD("get_propagation"), &BakedLightmap::get_propagation);
+
+ ClassDB::bind_method(D_METHOD("set_energy", "energy"), &BakedLightmap::set_energy);
+ ClassDB::bind_method(D_METHOD("get_energy"), &BakedLightmap::get_energy);
+
+ ClassDB::bind_method(D_METHOD("set_hdr", "hdr"), &BakedLightmap::set_hdr);
+ ClassDB::bind_method(D_METHOD("is_hdr"), &BakedLightmap::is_hdr);
+
+ ClassDB::bind_method(D_METHOD("set_image_path", "image_path"), &BakedLightmap::set_image_path);
+ ClassDB::bind_method(D_METHOD("get_image_path"), &BakedLightmap::get_image_path);
+
+ ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &BakedLightmap::bake, DEFVAL(Variant()), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("debug_bake"), &BakedLightmap::_debug_bake);
+ ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_subdiv", PROPERTY_HINT_ENUM, "128,256,512,1024,2048,4096"), "set_bake_subdiv", "get_bake_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "capture_subdiv", PROPERTY_HINT_ENUM, "128,256,512"), "set_capture_subdiv", "get_capture_subdiv");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_bake_quality", "get_bake_quality");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mode", PROPERTY_HINT_ENUM, "ConeTrace,RayTrace"), "set_bake_mode", "get_bake_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "energy", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_energy", "get_energy");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "is_hdr");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "image_path", PROPERTY_HINT_DIR), "set_image_path", "get_image_path");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedIndirectLightData"), "set_light_data", "get_light_data");
+
+ BIND_ENUM_CONSTANT(SUBDIV_128);
+ BIND_ENUM_CONSTANT(SUBDIV_256);
+ BIND_ENUM_CONSTANT(SUBDIV_512);
+ BIND_ENUM_CONSTANT(SUBDIV_1024);
+ BIND_ENUM_CONSTANT(SUBDIV_2048);
+ BIND_ENUM_CONSTANT(SUBDIV_4096);
+ BIND_ENUM_CONSTANT(SUBDIV_MAX);
+
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW);
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM);
+ BIND_ENUM_CONSTANT(BAKE_QUALITY_HIGH);
+ BIND_ENUM_CONSTANT(BAKE_MODE_CONE_TRACE);
+ BIND_ENUM_CONSTANT(BAKE_MODE_RAY_TRACE);
+}
+
+BakedLightmap::BakedLightmap() {
+
+ extents = Vector3(10, 10, 10);
+ bake_subdiv = SUBDIV_256;
+ capture_subdiv = SUBDIV_128;
+ bake_quality = BAKE_QUALITY_MEDIUM;
+ bake_mode = BAKE_MODE_CONE_TRACE;
+ energy = 1;
+ propagation = 1;
+ hdr = false;
+ image_path = ".";
+}
diff --git a/scene/3d/baked_lightmap.h b/scene/3d/baked_lightmap.h
new file mode 100644
index 0000000000..5595ec1e61
--- /dev/null
+++ b/scene/3d/baked_lightmap.h
@@ -0,0 +1,189 @@
+#ifndef BAKED_INDIRECT_LIGHT_H
+#define BAKED_INDIRECT_LIGHT_H
+
+#include "multimesh_instance.h"
+#include "scene/3d/light.h"
+#include "scene/3d/visual_instance.h"
+
+class BakedLightmapData : public Resource {
+ GDCLASS(BakedLightmapData, Resource);
+
+ RID baked_light;
+ AABB bounds;
+ float energy;
+ int cell_subdiv;
+ Transform cell_space_xform;
+
+ struct User {
+
+ NodePath path;
+ Ref<Texture> lightmap;
+ };
+
+ Vector<User> users;
+
+ void _set_user_data(const Array &p_data);
+ Array _get_user_data() const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_bounds(const AABB &p_bounds);
+ AABB get_bounds() const;
+
+ void set_octree(const PoolVector<uint8_t> &p_octree);
+ PoolVector<uint8_t> get_octree() const;
+
+ void set_cell_space_transform(const Transform &p_xform);
+ Transform get_cell_space_transform() const;
+
+ void set_cell_subdiv(int p_cell_subdiv);
+ int get_cell_subdiv() const;
+
+ void set_energy(float p_energy);
+ float get_energy() const;
+
+ void add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap);
+ int get_user_count() const;
+ NodePath get_user_path(int p_user) const;
+ Ref<Texture> get_user_lightmap(int p_user) const;
+ void clear_users();
+
+ virtual RID get_rid() const;
+ BakedLightmapData();
+ ~BakedLightmapData();
+};
+
+class BakedLightmap : public VisualInstance {
+ GDCLASS(BakedLightmap, VisualInstance);
+
+public:
+ enum Subdiv {
+ SUBDIV_128,
+ SUBDIV_256,
+ SUBDIV_512,
+ SUBDIV_1024,
+ SUBDIV_2048,
+ SUBDIV_4096,
+ SUBDIV_MAX
+
+ };
+
+ enum BakeQuality {
+ BAKE_QUALITY_LOW,
+ BAKE_QUALITY_MEDIUM,
+ BAKE_QUALITY_HIGH
+ };
+
+ enum BakeMode {
+ BAKE_MODE_CONE_TRACE,
+ BAKE_MODE_RAY_TRACE,
+ };
+
+ enum BakeError {
+ BAKE_ERROR_OK,
+ BAKE_ERROR_NO_SAVE_PATH,
+ BAKE_ERROR_NO_MESHES,
+ BAKE_ERROR_CANT_CREATE_IMAGE,
+ BAKE_ERROR_USER_ABORTED
+
+ };
+
+ typedef void (*BakeBeginFunc)(int);
+ typedef bool (*BakeStepFunc)(int, const String &);
+ typedef void (*BakeEndFunc)();
+
+private:
+ Subdiv bake_subdiv;
+ Subdiv capture_subdiv;
+ Vector3 extents;
+ float propagation;
+ float energy;
+ BakeQuality bake_quality;
+ BakeMode bake_mode;
+ bool hdr;
+ String image_path;
+
+ Ref<BakedLightmapData> light_data;
+
+ struct PlotMesh {
+ Ref<Material> override_material;
+ Vector<Ref<Material> > instance_materials;
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ NodePath path;
+ };
+
+ struct PlotLight {
+ Light *light;
+ Transform local_xform;
+ };
+
+ void _find_meshes_and_lights(Node *p_at_node, List<PlotMesh> &plot_meshes, List<PlotLight> &plot_lights);
+
+ void _debug_bake();
+
+ void _assign_lightmaps();
+ void _clear_lightmaps();
+
+ static bool _bake_time(void *ud, float p_secs, float p_progress);
+
+ struct BakeTimeData {
+ String text;
+ int pass;
+ uint64_t last_step;
+ };
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static BakeBeginFunc bake_begin_function;
+ static BakeStepFunc bake_step_function;
+ static BakeEndFunc bake_end_function;
+
+ void set_light_data(const Ref<BakedLightmapData> &p_data);
+ Ref<BakedLightmapData> get_light_data() const;
+
+ void set_bake_subdiv(Subdiv p_subdiv);
+ Subdiv get_bake_subdiv() const;
+
+ void set_capture_subdiv(Subdiv p_subdiv);
+ Subdiv get_capture_subdiv() const;
+
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_propagation(float p_propagation);
+ float get_propagation() const;
+
+ void set_energy(float p_energy);
+ float get_energy() const;
+
+ void set_bake_quality(BakeQuality p_quality);
+ BakeQuality get_bake_quality() const;
+
+ void set_bake_mode(BakeMode p_mode);
+ BakeMode get_bake_mode() const;
+
+ void set_hdr(bool p_enable);
+ bool is_hdr() const;
+
+ void set_image_path(const String &p_path);
+ String get_image_path() const;
+
+ AABB get_aabb() const;
+ PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ BakeError bake(Node *p_from_node, bool p_create_visual_debug = false);
+ BakedLightmap();
+};
+
+VARIANT_ENUM_CAST(BakedLightmap::Subdiv);
+VARIANT_ENUM_CAST(BakedLightmap::BakeQuality);
+VARIANT_ENUM_CAST(BakedLightmap::BakeMode);
+VARIANT_ENUM_CAST(BakedLightmap::BakeError);
+
+#endif // BAKED_INDIRECT_LIGHT_H
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index abc4a03498..72c0b979af 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -56,126 +56,16 @@ void Camera::_update_camera_mode() {
}
}
-bool Camera::_set(const StringName &p_name, const Variant &p_value) {
-
- bool changed_all = false;
- if (p_name == "projection") {
-
- int proj = p_value;
- if (proj == PROJECTION_PERSPECTIVE)
- mode = PROJECTION_PERSPECTIVE;
- if (proj == PROJECTION_ORTHOGONAL)
- mode = PROJECTION_ORTHOGONAL;
-
- changed_all = true;
- } else if (p_name == "fov" || p_name == "fovy" || p_name == "fovx")
- fov = p_value;
- else if (p_name == "size" || p_name == "sizex" || p_name == "sizey")
- size = p_value;
- else if (p_name == "near")
- near = p_value;
- else if (p_name == "far")
- far = p_value;
- else if (p_name == "keep_aspect")
- set_keep_aspect_mode(KeepAspect(int(p_value)));
- else if (p_name == "vaspect")
- set_keep_aspect_mode(p_value ? KEEP_WIDTH : KEEP_HEIGHT);
- else if (p_name == "h_offset")
- h_offset = p_value;
- else if (p_name == "v_offset")
- v_offset = p_value;
- else if (p_name == "current") {
- if (p_value.operator bool()) {
- make_current();
- } else {
- clear_current();
+void Camera::_validate_property(PropertyInfo &p_property) const {
+ if (p_property.name == "fov") {
+ if (mode == PROJECTION_ORTHOGONAL) {
+ p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
- } else if (p_name == "cull_mask") {
- set_cull_mask(p_value);
- } else if (p_name == "environment") {
- set_environment(p_value);
- } else if (p_name == "doppler/tracking") {
- set_doppler_tracking(DopplerTracking(int(p_value)));
- } else
- return false;
-
- _update_camera_mode();
- if (changed_all)
- _change_notify();
- return true;
-}
-bool Camera::_get(const StringName &p_name, Variant &r_ret) const {
-
- if (p_name == "projection") {
- r_ret = mode;
- } else if (p_name == "fov" || p_name == "fovy" || p_name == "fovx")
- r_ret = fov;
- else if (p_name == "size" || p_name == "sizex" || p_name == "sizey")
- r_ret = size;
- else if (p_name == "near")
- r_ret = near;
- else if (p_name == "far")
- r_ret = far;
- else if (p_name == "keep_aspect")
- r_ret = int(keep_aspect);
- else if (p_name == "current") {
-
- if (is_inside_tree() && get_tree()->is_node_being_edited(this)) {
- r_ret = current;
- } else {
- r_ret = is_current();
+ } else if (p_property.name == "size") {
+ if (mode == PROJECTION_PERSPECTIVE) {
+ p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
- } else if (p_name == "cull_mask") {
- r_ret = get_cull_mask();
- } else if (p_name == "h_offset") {
- r_ret = get_h_offset();
- } else if (p_name == "v_offset") {
- r_ret = get_v_offset();
- } else if (p_name == "environment") {
- r_ret = get_environment();
- } else if (p_name == "doppler/tracking") {
- r_ret = get_doppler_tracking();
- } else
- return false;
-
- return true;
-}
-
-void Camera::_get_property_list(List<PropertyInfo> *p_list) const {
-
- p_list->push_back(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"));
-
- switch (mode) {
-
- case PROJECTION_PERSPECTIVE: {
-
- p_list->push_back(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_NOEDITOR));
- if (keep_aspect == KEEP_WIDTH)
- p_list->push_back(PropertyInfo(Variant::REAL, "fovx", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_EDITOR));
- else
- p_list->push_back(PropertyInfo(Variant::REAL, "fovy", PROPERTY_HINT_RANGE, "1,179,0.1", PROPERTY_USAGE_EDITOR));
-
- } break;
- case PROJECTION_ORTHOGONAL: {
-
- p_list->push_back(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "1,16384,0.01", PROPERTY_USAGE_NOEDITOR));
- if (keep_aspect == KEEP_WIDTH)
- p_list->push_back(PropertyInfo(Variant::REAL, "sizex", PROPERTY_HINT_RANGE, "0.1,16384,0.01", PROPERTY_USAGE_EDITOR));
- else
- p_list->push_back(PropertyInfo(Variant::REAL, "sizey", PROPERTY_HINT_RANGE, "0.1,16384,0.01", PROPERTY_USAGE_EDITOR));
-
- } break;
}
-
- p_list->push_back(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01"));
- p_list->push_back(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01"));
- p_list->push_back(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"));
- p_list->push_back(PropertyInfo(Variant::BOOL, "current"));
- p_list->push_back(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER));
- p_list->push_back(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"));
- p_list->push_back(PropertyInfo(Variant::REAL, "h_offset"));
- p_list->push_back(PropertyInfo(Variant::REAL, "v_offset"));
- p_list->push_back(PropertyInfo(Variant::INT, "doppler/tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"));
}
void Camera::_update_camera() {
@@ -191,11 +81,12 @@ void Camera::_update_camera() {
get_viewport()->_camera_transform_changed_notify();
*/
- if (is_inside_tree() && is_current()) {
- get_viewport()->_camera_transform_changed_notify();
- }
+ if (!is_inside_tree() || get_tree()->is_node_being_edited(this) || !is_current())
+ return;
- if (is_current() && get_world().is_valid()) {
+ get_viewport()->_camera_transform_changed_notify();
+
+ if (get_world().is_valid()) {
get_world()->_update_camera(this);
}
}
@@ -281,6 +172,14 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) {
update_gizmo();
}
+void Camera::set_projection(Camera::Projection p_mode) {
+ if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) {
+ mode = p_mode;
+ _update_camera_mode();
+ _change_notify();
+ }
+}
+
RID Camera::get_camera() const {
return camera;
@@ -310,6 +209,14 @@ void Camera::clear_current() {
}
}
+void Camera::set_current(bool p_current) {
+ if (p_current) {
+ make_current();
+ } else {
+ clear_current();
+ }
+}
+
bool Camera::is_current() const {
if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) {
@@ -480,6 +387,7 @@ void Camera::set_environment(const Ref<Environment> &p_environment) {
VS::get_singleton()->camera_set_environment(camera, environment->get_rid());
else
VS::get_singleton()->camera_set_environment(camera, RID());
+ _update_camera_mode();
}
Ref<Environment> Camera::get_environment() const {
@@ -488,10 +396,9 @@ Ref<Environment> Camera::get_environment() const {
}
void Camera::set_keep_aspect_mode(KeepAspect p_aspect) {
-
keep_aspect = p_aspect;
VisualServer::get_singleton()->camera_set_use_vertical_aspect(camera, p_aspect == KEEP_WIDTH);
-
+ _update_camera_mode();
_change_notify();
}
@@ -500,6 +407,15 @@ Camera::KeepAspect Camera::get_keep_aspect_mode() const {
return keep_aspect;
}
+void Camera::set_vaspect(bool p_vaspect) {
+ set_keep_aspect_mode(p_vaspect ? KEEP_WIDTH : KEEP_HEIGHT);
+ _update_camera_mode();
+}
+
+bool Camera::get_vaspect() const {
+ return keep_aspect == KEEP_HEIGHT;
+}
+
void Camera::set_doppler_tracking(DopplerTracking p_tracking) {
if (doppler_tracking == p_tracking)
@@ -510,6 +426,7 @@ void Camera::set_doppler_tracking(DopplerTracking p_tracking) {
velocity_tracker->set_track_physics_step(doppler_tracking == DOPPLER_TRACKING_PHYSICS_STEP);
velocity_tracker->reset(get_global_transform().origin);
}
+ _update_camera_mode();
}
Camera::DopplerTracking Camera::get_doppler_tracking() const {
@@ -528,13 +445,19 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal);
ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current);
ClassDB::bind_method(D_METHOD("clear_current"), &Camera::clear_current);
+ ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov);
ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size);
ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar);
ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear);
+ ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov);
+ ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size);
+ ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar);
+ ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear);
ClassDB::bind_method(D_METHOD("get_projection"), &Camera::get_projection);
+ ClassDB::bind_method(D_METHOD("set_projection"), &Camera::set_projection);
ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera::set_h_offset);
ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera::get_h_offset);
ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera::set_v_offset);
@@ -545,10 +468,26 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_environment"), &Camera::get_environment);
ClassDB::bind_method(D_METHOD("set_keep_aspect_mode", "mode"), &Camera::set_keep_aspect_mode);
ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera::get_keep_aspect_mode);
+ ClassDB::bind_method(D_METHOD("set_vaspect"), &Camera::set_vaspect);
+ ClassDB::bind_method(D_METHOD("get_vaspect"), &Camera::get_vaspect);
ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera::set_doppler_tracking);
ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera::get_doppler_tracking);
//ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current );
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vaspect"), "set_vaspect", "get_vaspect");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "near"), "set_znear", "get_znear");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "far"), "set_zfar", "get_zfar");
+
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
@@ -585,10 +524,30 @@ Camera::Projection Camera::get_projection() const {
return mode;
}
-void Camera::set_cull_mask(uint32_t p_layers) {
+void Camera::set_fov(float p_fov) {
+ fov = p_fov;
+ _update_camera_mode();
+}
+
+void Camera::set_size(float p_size) {
+ size = p_size;
+ _update_camera_mode();
+}
+void Camera::set_znear(float p_znear) {
+ near = p_znear;
+ _update_camera_mode();
+}
+
+void Camera::set_zfar(float p_zfar) {
+ far = p_zfar;
+ _update_camera_mode();
+}
+
+void Camera::set_cull_mask(uint32_t p_layers) {
layers = p_layers;
VisualServer::get_singleton()->camera_set_cull_mask(camera, layers);
+ _update_camera_mode();
}
uint32_t Camera::get_cull_mask() const {
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 73c6844c1a..520afb962b 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -95,10 +95,8 @@ protected:
virtual void _request_camera_update();
void _update_camera_mode();
- 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;
void _notification(int p_what);
+ virtual void _validate_property(PropertyInfo &property) const;
static void _bind_methods();
@@ -111,9 +109,11 @@ public:
void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
void set_orthogonal(float p_size, float p_z_near, float p_z_far);
+ void set_projection(Camera::Projection p_mode);
void make_current();
void clear_current();
+ void set_current(bool p_current);
bool is_current() const;
RID get_camera() const;
@@ -124,6 +124,11 @@ public:
float get_znear() const;
Projection get_projection() const;
+ void set_fov(float p_fov);
+ void set_size(float p_size);
+ void set_zfar(float p_zfar);
+ void set_znear(float p_znear);
+
virtual Transform get_camera_transform() const;
Vector3 project_ray_normal(const Point2 &p_pos) const;
@@ -143,6 +148,8 @@ public:
void set_keep_aspect_mode(KeepAspect p_aspect);
KeepAspect get_keep_aspect_mode() const;
+ void set_vaspect(bool p_vaspect);
+ bool get_vaspect() const;
void set_v_offset(float p_offset);
float get_v_offset() const;
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index 2754d56a05..9c811a74bf 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -30,6 +30,7 @@
#include "gi_probe.h"
#include "mesh_instance.h"
+#include "voxel_light_baker.h"
void GIProbeData::set_bounds(const AABB &p_bounds) {
@@ -329,754 +330,7 @@ bool GIProbe::is_compressed() const {
return compress;
}
-#include "math.h"
-
-#define FINDMINMAX(x0, x1, x2, min, max) \
- min = max = x0; \
- if (x1 < min) min = x1; \
- if (x1 > max) max = x1; \
- if (x2 < min) min = x2; \
- if (x2 > max) max = x2;
-
-static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
- int q;
- Vector3 vmin, vmax;
- for (q = 0; q <= 2; q++) {
- if (normal[q] > 0.0f) {
- vmin[q] = -maxbox[q];
- vmax[q] = maxbox[q];
- } else {
- vmin[q] = maxbox[q];
- vmax[q] = -maxbox[q];
- }
- }
- if (normal.dot(vmin) + d > 0.0f) return false;
- if (normal.dot(vmax) + d >= 0.0f) return true;
-
- return false;
-}
-
-/*======================== X-tests ========================*/
-#define AXISTEST_X01(a, b, fa, fb) \
- p0 = a * v0.y - b * v0.z; \
- p2 = a * v2.y - b * v2.z; \
- if (p0 < p2) { \
- min = p0; \
- max = p2; \
- } else { \
- min = p2; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_X2(a, b, fa, fb) \
- p0 = a * v0.y - b * v0.z; \
- p1 = a * v1.y - b * v1.z; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-/*======================== Y-tests ========================*/
-#define AXISTEST_Y02(a, b, fa, fb) \
- p0 = -a * v0.x + b * v0.z; \
- p2 = -a * v2.x + b * v2.z; \
- if (p0 < p2) { \
- min = p0; \
- max = p2; \
- } else { \
- min = p2; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_Y1(a, b, fa, fb) \
- p0 = -a * v0.x + b * v0.z; \
- p1 = -a * v1.x + b * v1.z; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
- if (min > rad || max < -rad) return false;
-
-/*======================== Z-tests ========================*/
-
-#define AXISTEST_Z12(a, b, fa, fb) \
- p1 = a * v1.x - b * v1.y; \
- p2 = a * v2.x - b * v2.y; \
- if (p2 < p1) { \
- min = p2; \
- max = p1; \
- } else { \
- min = p1; \
- max = p2; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
- if (min > rad || max < -rad) return false;
-
-#define AXISTEST_Z0(a, b, fa, fb) \
- p0 = a * v0.x - b * v0.y; \
- p1 = a * v1.x - b * v1.y; \
- if (p0 < p1) { \
- min = p0; \
- max = p1; \
- } else { \
- min = p1; \
- max = p0; \
- } \
- rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
- if (min > rad || max < -rad) return false;
-
-static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
-
- /* use separating axis theorem to test overlap between triangle and box */
- /* need to test for overlap in these directions: */
- /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
- /* we do not even need to test these) */
- /* 2) normal of the triangle */
- /* 3) crossproduct(edge from tri, {x,y,z}-directin) */
- /* this gives 3x3=9 more tests */
- Vector3 v0, v1, v2;
- float min, max, d, p0, p1, p2, rad, fex, fey, fez;
- Vector3 normal, e0, e1, e2;
-
- /* This is the fastest branch on Sun */
- /* move everything so that the boxcenter is in (0,0,0) */
-
- v0 = triverts[0] - boxcenter;
- v1 = triverts[1] - boxcenter;
- v2 = triverts[2] - boxcenter;
-
- /* compute triangle edges */
- e0 = v1 - v0; /* tri edge 0 */
- e1 = v2 - v1; /* tri edge 1 */
- e2 = v0 - v2; /* tri edge 2 */
-
- /* Bullet 3: */
- /* test the 9 tests first (this was faster) */
- fex = Math::abs(e0.x);
- fey = Math::abs(e0.y);
- fez = Math::abs(e0.z);
- AXISTEST_X01(e0.z, e0.y, fez, fey);
- AXISTEST_Y02(e0.z, e0.x, fez, fex);
- AXISTEST_Z12(e0.y, e0.x, fey, fex);
-
- fex = Math::abs(e1.x);
- fey = Math::abs(e1.y);
- fez = Math::abs(e1.z);
- AXISTEST_X01(e1.z, e1.y, fez, fey);
- AXISTEST_Y02(e1.z, e1.x, fez, fex);
- AXISTEST_Z0(e1.y, e1.x, fey, fex);
-
- fex = Math::abs(e2.x);
- fey = Math::abs(e2.y);
- fez = Math::abs(e2.z);
- AXISTEST_X2(e2.z, e2.y, fez, fey);
- AXISTEST_Y1(e2.z, e2.x, fez, fex);
- AXISTEST_Z12(e2.y, e2.x, fey, fex);
-
- /* Bullet 1: */
- /* first test overlap in the {x,y,z}-directions */
- /* find min, max of the triangle each direction, and test for overlap in */
- /* that direction -- this is equivalent to testing a minimal AABB around */
- /* the triangle against the AABB */
-
- /* test in X-direction */
- FINDMINMAX(v0.x, v1.x, v2.x, min, max);
- if (min > boxhalfsize.x || max < -boxhalfsize.x) return false;
-
- /* test in Y-direction */
- FINDMINMAX(v0.y, v1.y, v2.y, min, max);
- if (min > boxhalfsize.y || max < -boxhalfsize.y) return false;
-
- /* test in Z-direction */
- FINDMINMAX(v0.z, v1.z, v2.z, min, max);
- if (min > boxhalfsize.z || max < -boxhalfsize.z) return false;
-
- /* Bullet 2: */
- /* test if the box intersects the plane of the triangle */
- /* compute plane equation of triangle: normal*x+d=0 */
- normal = e0.cross(e1);
- d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
- if (!planeBoxOverlap(normal, d, boxhalfsize)) return false;
-
- return true; /* box and triangle overlaps */
-}
-
-static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv) {
-
- if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2)
- return p_uv[0];
- if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2)
- return p_uv[1];
- if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2)
- return p_uv[2];
-
- Vector3 v0 = p_vtx[1] - p_vtx[0];
- Vector3 v1 = p_vtx[2] - p_vtx[0];
- Vector3 v2 = p_pos - p_vtx[0];
-
- float d00 = v0.dot(v0);
- float d01 = v0.dot(v1);
- float d11 = v1.dot(v1);
- float d20 = v2.dot(v0);
- float d21 = v2.dot(v1);
- float denom = (d00 * d11 - d01 * d01);
- if (denom == 0)
- return p_uv[0];
- float v = (d11 * d20 - d01 * d21) / denom;
- float w = (d00 * d21 - d01 * d20) / denom;
- float u = 1.0f - v - w;
-
- return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
-}
-
-void GIProbe::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const AABB &p_aabb, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
- //plot the face by guessing it's albedo and emission value
-
- //find best axis to map to, for scanning values
- int closest_axis = 0;
- float closest_dot = 0;
-
- Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]);
- Vector3 normal = plane.normal;
-
- for (int i = 0; i < 3; i++) {
-
- Vector3 axis;
- axis[i] = 1.0;
- float dot = ABS(normal.dot(axis));
- if (i == 0 || dot > closest_dot) {
- closest_axis = i;
- closest_dot = dot;
- }
- }
-
- Vector3 axis;
- axis[closest_axis] = 1.0;
- Vector3 t1;
- t1[(closest_axis + 1) % 3] = 1.0;
- Vector3 t2;
- t2[(closest_axis + 2) % 3] = 1.0;
-
- t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width);
- t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width);
-
- Color albedo_accum;
- Color emission_accum;
- Vector3 normal_accum;
-
- float alpha = 0.0;
-
- //map to a grid average in the best axis for this face
- for (int i = 0; i < color_scan_cell_width; i++) {
-
- Vector3 ofs_i = float(i) * t1;
-
- for (int j = 0; j < color_scan_cell_width; j++) {
-
- Vector3 ofs_j = float(j) * t2;
-
- Vector3 from = p_aabb.position + ofs_i + ofs_j;
- Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis];
- Vector3 half = (to - from) * 0.5;
-
- //is in this cell?
- if (!fast_tri_box_overlap(from + half, half, p_vtx)) {
- continue; //face does not span this cell
- }
-
- //go from -size to +size*2 to avoid skipping collisions
- Vector3 ray_from = from + (t1 + t2) * 0.5 - axis * p_aabb.size[closest_axis];
- Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis] * 2;
-
- if (normal.dot(ray_from - ray_to) < 0) {
- SWAP(ray_from, ray_to);
- }
-
- Vector3 intersection;
-
- if (!plane.intersects_segment(ray_from, ray_to, &intersection)) {
- if (ABS(plane.distance_to(ray_from)) < ABS(plane.distance_to(ray_to))) {
- intersection = plane.project(ray_from);
- } else {
-
- intersection = plane.project(ray_to);
- }
- }
-
- intersection = Face3(p_vtx[0], p_vtx[1], p_vtx[2]).get_closest_point_to(intersection);
-
- Vector2 uv = get_uv(intersection, p_vtx, p_uv);
-
- int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
- int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
-
- int ofs = uv_y * bake_texture_size + uv_x;
- albedo_accum.r += p_material.albedo[ofs].r;
- albedo_accum.g += p_material.albedo[ofs].g;
- albedo_accum.b += p_material.albedo[ofs].b;
- albedo_accum.a += p_material.albedo[ofs].a;
-
- emission_accum.r += p_material.emission[ofs].r;
- emission_accum.g += p_material.emission[ofs].g;
- emission_accum.b += p_material.emission[ofs].b;
-
- normal_accum += normal;
-
- alpha += 1.0;
- }
- }
-
- if (alpha == 0) {
- //could not in any way get texture information.. so use closest point to center
-
- Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]);
- Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5);
-
- Vector2 uv = get_uv(inters, p_vtx, p_uv);
-
- int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
- int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
-
- int ofs = uv_y * bake_texture_size + uv_x;
-
- alpha = 1.0 / (color_scan_cell_width * color_scan_cell_width);
-
- albedo_accum.r = p_material.albedo[ofs].r * alpha;
- albedo_accum.g = p_material.albedo[ofs].g * alpha;
- albedo_accum.b = p_material.albedo[ofs].b * alpha;
- albedo_accum.a = p_material.albedo[ofs].a * alpha;
-
- emission_accum.r = p_material.emission[ofs].r * alpha;
- emission_accum.g = p_material.emission[ofs].g * alpha;
- emission_accum.b = p_material.emission[ofs].b * alpha;
-
- normal_accum *= alpha;
-
- } else {
-
- float accdiv = 1.0 / (color_scan_cell_width * color_scan_cell_width);
- alpha *= accdiv;
-
- albedo_accum.r *= accdiv;
- albedo_accum.g *= accdiv;
- albedo_accum.b *= accdiv;
- albedo_accum.a *= accdiv;
-
- emission_accum.r *= accdiv;
- emission_accum.g *= accdiv;
- emission_accum.b *= accdiv;
-
- normal_accum *= accdiv;
- }
-
- //put this temporarily here, corrected in a later step
- p_baker->bake_cells[p_idx].albedo[0] += albedo_accum.r;
- p_baker->bake_cells[p_idx].albedo[1] += albedo_accum.g;
- p_baker->bake_cells[p_idx].albedo[2] += albedo_accum.b;
- p_baker->bake_cells[p_idx].emission[0] += emission_accum.r;
- p_baker->bake_cells[p_idx].emission[1] += emission_accum.g;
- p_baker->bake_cells[p_idx].emission[2] += emission_accum.b;
- p_baker->bake_cells[p_idx].normal[0] += normal_accum.x;
- p_baker->bake_cells[p_idx].normal[1] += normal_accum.y;
- p_baker->bake_cells[p_idx].normal[2] += normal_accum.z;
- p_baker->bake_cells[p_idx].alpha += alpha;
-
- } else {
- //go down
-
- int half = (1 << (p_baker->cell_subdiv - 1)) >> (p_level + 1);
- for (int i = 0; i < 8; i++) {
-
- AABB aabb = p_aabb;
- aabb.size *= 0.5;
-
- int nx = p_x;
- int ny = p_y;
- int nz = p_z;
-
- if (i & 1) {
- aabb.position.x += aabb.size.x;
- nx += half;
- }
- if (i & 2) {
- aabb.position.y += aabb.size.y;
- ny += half;
- }
- if (i & 4) {
- aabb.position.z += aabb.size.z;
- nz += half;
- }
- //make sure to not plot beyond limits
- if (nx < 0 || nx >= p_baker->axis_cell_size[0] || ny < 0 || ny >= p_baker->axis_cell_size[1] || nz < 0 || nz >= p_baker->axis_cell_size[2])
- continue;
-
- {
- AABB test_aabb = aabb;
- //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
- Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
-
- if (!fast_tri_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
- //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
- //does not fit in child, go on
- continue;
- }
- }
-
- if (p_baker->bake_cells[p_idx].childs[i] == Baker::CHILD_EMPTY) {
- //sub cell must be created
-
- uint32_t child_idx = p_baker->bake_cells.size();
- p_baker->bake_cells[p_idx].childs[i] = child_idx;
- p_baker->bake_cells.resize(p_baker->bake_cells.size() + 1);
- p_baker->bake_cells[child_idx].level = p_level + 1;
- }
-
- _plot_face(p_baker->bake_cells[p_idx].childs[i], p_level + 1, nx, ny, nz, p_vtx, p_uv, p_material, aabb, p_baker);
- }
- }
-}
-
-void GIProbe::_fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
-
- p_baker->leaf_voxel_count++;
- float alpha = p_baker->bake_cells[p_idx].alpha;
-
- p_baker->bake_cells[p_idx].albedo[0] /= alpha;
- p_baker->bake_cells[p_idx].albedo[1] /= alpha;
- p_baker->bake_cells[p_idx].albedo[2] /= alpha;
-
- //transfer emission to light
- p_baker->bake_cells[p_idx].emission[0] /= alpha;
- p_baker->bake_cells[p_idx].emission[1] /= alpha;
- p_baker->bake_cells[p_idx].emission[2] /= alpha;
-
- p_baker->bake_cells[p_idx].normal[0] /= alpha;
- p_baker->bake_cells[p_idx].normal[1] /= alpha;
- p_baker->bake_cells[p_idx].normal[2] /= alpha;
-
- Vector3 n(p_baker->bake_cells[p_idx].normal[0], p_baker->bake_cells[p_idx].normal[1], p_baker->bake_cells[p_idx].normal[2]);
- if (n.length() < 0.01) {
- //too much fight over normal, zero it
- p_baker->bake_cells[p_idx].normal[0] = 0;
- p_baker->bake_cells[p_idx].normal[1] = 0;
- p_baker->bake_cells[p_idx].normal[2] = 0;
- } else {
- n.normalize();
- p_baker->bake_cells[p_idx].normal[0] = n.x;
- p_baker->bake_cells[p_idx].normal[1] = n.y;
- p_baker->bake_cells[p_idx].normal[2] = n.z;
- }
-
- p_baker->bake_cells[p_idx].alpha = 1.0;
-
- /*
- //remove neighbours from used sides
-
- for(int n=0;n<6;n++) {
-
- int ofs[3]={0,0,0};
-
- ofs[n/2]=(n&1)?1:-1;
-
- //convert to x,y,z on this level
- int x=p_x;
- int y=p_y;
- int z=p_z;
-
- x+=ofs[0];
- y+=ofs[1];
- z+=ofs[2];
-
- int ofs_x=0;
- int ofs_y=0;
- int ofs_z=0;
- int size = 1<<p_level;
- int half=size/2;
-
-
- if (x<0 || x>=size || y<0 || y>=size || z<0 || z>=size) {
- //neighbour is out, can't use it
- p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n));
- continue;
- }
-
- uint32_t neighbour=0;
-
- for(int i=0;i<p_baker->cell_subdiv-1;i++) {
-
- Baker::Cell *bc = &p_baker->bake_cells[neighbour];
-
- int child = 0;
- if (x >= ofs_x + half) {
- child|=1;
- ofs_x+=half;
- }
- if (y >= ofs_y + half) {
- child|=2;
- ofs_y+=half;
- }
- if (z >= ofs_z + half) {
- child|=4;
- ofs_z+=half;
- }
-
- neighbour = bc->childs[child];
- if (neighbour==Baker::CHILD_EMPTY) {
- break;
- }
-
- half>>=1;
- }
-
- if (neighbour!=Baker::CHILD_EMPTY) {
- p_baker->bake_cells[p_idx].used_sides&=~(1<<uint32_t(n));
- }
- }
- */
- } else {
-
- //go down
-
- float alpha_average = 0;
- int half = (1 << (p_baker->cell_subdiv - 1)) >> (p_level + 1);
- for (int i = 0; i < 8; i++) {
-
- uint32_t child = p_baker->bake_cells[p_idx].childs[i];
-
- if (child == Baker::CHILD_EMPTY)
- continue;
-
- int nx = p_x;
- int ny = p_y;
- int nz = p_z;
-
- if (i & 1)
- nx += half;
- if (i & 2)
- ny += half;
- if (i & 4)
- nz += half;
-
- _fixup_plot(child, p_level + 1, nx, ny, nz, p_baker);
- alpha_average += p_baker->bake_cells[child].alpha;
- }
-
- p_baker->bake_cells[p_idx].alpha = alpha_average / 8.0;
- p_baker->bake_cells[p_idx].emission[0] = 0;
- p_baker->bake_cells[p_idx].emission[1] = 0;
- p_baker->bake_cells[p_idx].emission[2] = 0;
- p_baker->bake_cells[p_idx].normal[0] = 0;
- p_baker->bake_cells[p_idx].normal[1] = 0;
- p_baker->bake_cells[p_idx].normal[2] = 0;
- p_baker->bake_cells[p_idx].albedo[0] = 0;
- p_baker->bake_cells[p_idx].albedo[1] = 0;
- p_baker->bake_cells[p_idx].albedo[2] = 0;
- }
-}
-
-Vector<Color> GIProbe::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) {
-
- Vector<Color> ret;
-
- if (p_image.is_null() || p_image->empty()) {
-
- ret.resize(bake_texture_size * bake_texture_size);
- for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
- ret[i] = p_color_add;
- }
-
- return ret;
- }
- p_image = p_image->duplicate();
-
- if (p_image->is_compressed()) {
- print_line("DECOMPRESSING!!!!");
-
- p_image->decompress();
- }
- p_image->convert(Image::FORMAT_RGBA8);
- p_image->resize(bake_texture_size, bake_texture_size, Image::INTERPOLATE_CUBIC);
-
- PoolVector<uint8_t>::Read r = p_image->get_data().read();
- ret.resize(bake_texture_size * bake_texture_size);
-
- for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
- Color c;
- c.r = (r[i * 4 + 0] / 255.0) * p_color_mul.r + p_color_add.r;
- c.g = (r[i * 4 + 1] / 255.0) * p_color_mul.g + p_color_add.g;
- c.b = (r[i * 4 + 2] / 255.0) * p_color_mul.b + p_color_add.b;
-
- c.a = r[i * 4 + 3] / 255.0;
-
- ret[i] = c;
- }
-
- return ret;
-}
-
-GIProbe::Baker::MaterialCache GIProbe::_get_material_cache(Ref<Material> p_material, Baker *p_baker) {
-
- //this way of obtaining materials is inaccurate and also does not support some compressed formats very well
- Ref<SpatialMaterial> mat = p_material;
-
- Ref<Material> material = mat; //hack for now
-
- if (p_baker->material_cache.has(material)) {
- return p_baker->material_cache[material];
- }
-
- Baker::MaterialCache mc;
-
- if (mat.is_valid()) {
-
- Ref<Texture> albedo_tex = mat->get_texture(SpatialMaterial::TEXTURE_ALBEDO);
-
- Ref<Image> img_albedo;
- if (albedo_tex.is_valid()) {
-
- img_albedo = albedo_tex->get_data();
- mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative
- } else {
- mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive
- }
-
- Ref<Texture> emission_tex = mat->get_texture(SpatialMaterial::TEXTURE_EMISSION);
-
- Color emission_col = mat->get_emission();
- float emission_energy = mat->get_emission_energy();
-
- Ref<Image> img_emission;
-
- if (emission_tex.is_valid()) {
-
- img_emission = emission_tex->get_data();
- }
-
- if (mat->get_emission_operator() == SpatialMaterial::EMISSION_OP_ADD) {
- mc.emission = _get_bake_texture(img_emission, Color(1, 1, 1) * emission_energy, emission_col * emission_energy);
- } else {
- mc.emission = _get_bake_texture(img_emission, emission_col * emission_energy, Color(0, 0, 0));
- }
-
- } else {
- Ref<Image> empty;
-
- mc.albedo = _get_bake_texture(empty, Color(0, 0, 0), Color(1, 1, 1));
- mc.emission = _get_bake_texture(empty, Color(0, 0, 0), Color(0, 0, 0));
- }
-
- p_baker->material_cache[p_material] = mc;
- return mc;
-}
-
-void GIProbe::_plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) {
-
- for (int i = 0; i < p_mesh->get_surface_count(); i++) {
-
- if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
- continue; //only triangles
-
- Ref<Material> src_material;
-
- if (p_override_material.is_valid()) {
- src_material = p_override_material;
- } else if (i < p_materials.size() && p_materials[i].is_valid()) {
- src_material = p_materials[i];
- } else {
- src_material = p_mesh->surface_get_material(i);
- }
- Baker::MaterialCache material = _get_material_cache(src_material, p_baker);
-
- Array a = p_mesh->surface_get_arrays(i);
-
- PoolVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
- PoolVector<Vector3>::Read vr = vertices.read();
- PoolVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV];
- PoolVector<Vector2>::Read uvr;
- PoolVector<int> index = a[Mesh::ARRAY_INDEX];
-
- bool read_uv = false;
-
- if (uv.size()) {
-
- uvr = uv.read();
- read_uv = true;
- }
-
- if (index.size()) {
-
- int facecount = index.size() / 3;
- PoolVector<int>::Read ir = index.read();
-
- for (int j = 0; j < facecount; j++) {
-
- Vector3 vtxs[3];
- Vector2 uvs[3];
-
- for (int k = 0; k < 3; k++) {
- vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]);
- }
-
- if (read_uv) {
- for (int k = 0; k < 3; k++) {
- uvs[k] = uvr[ir[j * 3 + k]];
- }
- }
-
- //test against original bounds
- if (!fast_tri_box_overlap(-extents, extents * 2, vtxs))
- continue;
- //plot
- _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, p_baker->po2_bounds, p_baker);
- }
-
- } else {
-
- int facecount = vertices.size() / 3;
-
- for (int j = 0; j < facecount; j++) {
-
- Vector3 vtxs[3];
- Vector2 uvs[3];
-
- for (int k = 0; k < 3; k++) {
- vtxs[k] = p_xform.xform(vr[j * 3 + k]);
- }
-
- if (read_uv) {
- for (int k = 0; k < 3; k++) {
- uvs[k] = uvr[j * 3 + k];
- }
- }
-
- //test against original bounds
- if (!fast_tri_box_overlap(-extents, extents * 2, vtxs))
- continue;
- //plot face
- _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, p_baker->po2_bounds, p_baker);
- }
- }
- }
-}
-
-void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
+void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) {
MeshInstance *mi = Object::cast_to<MeshInstance>(p_at_node);
if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT) && mi->is_visible_in_tree()) {
@@ -1088,14 +342,14 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
- Baker::PlotMesh pm;
+ PlotMesh pm;
pm.local_xform = xf;
pm.mesh = mesh;
for (int i = 0; i < mesh->get_surface_count(); i++) {
pm.instance_materials.push_back(mi->get_surface_material(i));
}
pm.override_material = mi->get_material_override();
- p_baker->mesh_list.push_back(pm);
+ plot_meshes.push_back(pm);
}
}
}
@@ -1118,10 +372,10 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
Transform xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf);
if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) {
- Baker::PlotMesh pm;
+ PlotMesh pm;
pm.local_xform = xf;
pm.mesh = mesh;
- p_baker->mesh_list.push_back(pm);
+ plot_meshes.push_back(pm);
}
}
}
@@ -1133,7 +387,7 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) {
if (!child->get_owner())
continue; //maybe a helper
- _find_meshes(child, p_baker);
+ _find_meshes(child, plot_meshes);
}
}
@@ -1143,151 +397,65 @@ GIProbe::BakeEndFunc GIProbe::bake_end_function = NULL;
void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
- Baker baker;
-
static const int subdiv_value[SUBDIV_MAX] = { 7, 8, 9, 10 };
- baker.cell_subdiv = subdiv_value[subdiv];
- baker.bake_cells.resize(1);
-
- //find out the actual real bounds, power of 2, which gets the highest subdivision
- baker.po2_bounds = AABB(-extents, extents * 2.0);
- int longest_axis = baker.po2_bounds.get_longest_axis_index();
- baker.axis_cell_size[longest_axis] = (1 << (baker.cell_subdiv - 1));
- baker.leaf_voxel_count = 0;
-
- for (int i = 0; i < 3; i++) {
-
- if (i == longest_axis)
- continue;
-
- baker.axis_cell_size[i] = baker.axis_cell_size[longest_axis];
- float axis_size = baker.po2_bounds.size[longest_axis];
-
- //shrink until fit subdiv
- while (axis_size / 2.0 >= baker.po2_bounds.size[i]) {
- axis_size /= 2.0;
- baker.axis_cell_size[i] >>= 1;
- }
-
- baker.po2_bounds.size[i] = baker.po2_bounds.size[longest_axis];
- }
+ VoxelLightBaker baker;
- Transform to_bounds;
- to_bounds.basis.scale(Vector3(baker.po2_bounds.size[longest_axis], baker.po2_bounds.size[longest_axis], baker.po2_bounds.size[longest_axis]));
- to_bounds.origin = baker.po2_bounds.position;
+ baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0));
- Transform to_grid;
- to_grid.basis.scale(Vector3(baker.axis_cell_size[longest_axis], baker.axis_cell_size[longest_axis], baker.axis_cell_size[longest_axis]));
+ List<PlotMesh> mesh_list;
- baker.to_cell_space = to_grid * to_bounds.affine_inverse();
-
- _find_meshes(p_from_node ? p_from_node : get_parent(), &baker);
+ _find_meshes(p_from_node ? p_from_node : get_parent(), mesh_list);
if (bake_begin_function) {
- bake_begin_function(baker.mesh_list.size() + 1);
+ bake_begin_function(mesh_list.size() + 1);
}
int pmc = 0;
- for (List<Baker::PlotMesh>::Element *E = baker.mesh_list.front(); E; E = E->next()) {
+ for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) {
if (bake_step_function) {
- bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(baker.mesh_list.size()));
+ bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(mesh_list.size()));
}
pmc++;
- _plot_mesh(E->get().local_xform, E->get().mesh, &baker, E->get().instance_materials, E->get().override_material);
+ baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material);
}
if (bake_step_function) {
bake_step_function(pmc++, RTR("Finishing Plot"));
}
- _fixup_plot(0, 0, 0, 0, 0, &baker);
+ baker.end_bake();
//create the data for visual server
- PoolVector<int> data;
-
- data.resize(16 + (8 + 1 + 1 + 1 + 1) * baker.bake_cells.size()); //4 for header, rest for rest.
-
- {
- PoolVector<int>::Write w = data.write();
-
- uint32_t *w32 = (uint32_t *)w.ptr();
-
- w32[0] = 0; //version
- w32[1] = baker.cell_subdiv; //subdiv
- w32[2] = baker.axis_cell_size[0];
- w32[3] = baker.axis_cell_size[1];
- w32[4] = baker.axis_cell_size[2];
- w32[5] = baker.bake_cells.size();
- w32[6] = baker.leaf_voxel_count;
-
- int ofs = 16;
+ PoolVector<int> data = baker.create_gi_probe_data();
- for (int i = 0; i < baker.bake_cells.size(); i++) {
-
- for (int j = 0; j < 8; j++) {
- w32[ofs++] = baker.bake_cells[i].childs[j];
- }
-
- { //albedo
- uint32_t rgba = uint32_t(CLAMP(baker.bake_cells[i].albedo[0] * 255.0, 0, 255)) << 16;
- rgba |= uint32_t(CLAMP(baker.bake_cells[i].albedo[1] * 255.0, 0, 255)) << 8;
- rgba |= uint32_t(CLAMP(baker.bake_cells[i].albedo[2] * 255.0, 0, 255)) << 0;
-
- w32[ofs++] = rgba;
- }
- { //emission
-
- Vector3 e(baker.bake_cells[i].emission[0], baker.bake_cells[i].emission[1], baker.bake_cells[i].emission[2]);
- float l = e.length();
- if (l > 0) {
- e.normalize();
- l = CLAMP(l / 8.0, 0, 1.0);
- }
-
- uint32_t em = uint32_t(CLAMP(e[0] * 255, 0, 255)) << 24;
- em |= uint32_t(CLAMP(e[1] * 255, 0, 255)) << 16;
- em |= uint32_t(CLAMP(e[2] * 255, 0, 255)) << 8;
- em |= uint32_t(CLAMP(l * 255, 0, 255));
-
- w32[ofs++] = em;
- }
-
- //w32[ofs++]=baker.bake_cells[i].used_sides;
- { //normal
-
- Vector3 n(baker.bake_cells[i].normal[0], baker.bake_cells[i].normal[1], baker.bake_cells[i].normal[2]);
- n = n * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
- uint32_t norm = 0;
-
- norm |= uint32_t(CLAMP(n.x * 255.0, 0, 255)) << 16;
- norm |= uint32_t(CLAMP(n.y * 255.0, 0, 255)) << 8;
- norm |= uint32_t(CLAMP(n.z * 255.0, 0, 255)) << 0;
-
- w32[ofs++] = norm;
- }
-
- {
- uint16_t alpha = CLAMP(uint32_t(baker.bake_cells[i].alpha * 65535.0), 0, 65535);
- uint16_t level = baker.bake_cells[i].level;
-
- w32[ofs++] = (uint32_t(level) << 16) | uint32_t(alpha);
- }
+ if (p_create_visual_debug) {
+ MultiMeshInstance *mmi = memnew(MultiMeshInstance);
+ mmi->set_multimesh(baker.create_debug_multimesh());
+ add_child(mmi);
+#ifdef TOOLS_ENABLED
+ if (get_tree()->get_edited_scene_root() == this) {
+ mmi->set_owner(this);
+ } else {
+ mmi->set_owner(get_owner());
}
- }
+#else
+ mmi->set_owner(get_owner());
+#endif
- if (p_create_visual_debug) {
- _create_debug_mesh(&baker);
} else {
- Ref<GIProbeData> probe_data;
- probe_data.instance();
+ Ref<GIProbeData> probe_data = get_probe_data();
+
+ if (probe_data.is_null())
+ probe_data.instance();
+
probe_data->set_bounds(AABB(-extents, extents * 2.0));
- probe_data->set_cell_size(baker.po2_bounds.size[longest_axis] / baker.axis_cell_size[longest_axis]);
+ probe_data->set_cell_size(baker.get_cell_size());
probe_data->set_dynamic_data(data);
probe_data->set_dynamic_range(dynamic_range);
probe_data->set_energy(energy);
@@ -1296,7 +464,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
probe_data->set_propagation(propagation);
probe_data->set_interior(interior);
probe_data->set_compress(compress);
- probe_data->set_to_cell_xform(baker.to_cell_space);
+ probe_data->set_to_cell_xform(baker.get_to_cell_space_xform());
set_probe_data(probe_data);
}
@@ -1306,135 +474,6 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) {
}
}
-void GIProbe::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, Baker *p_baker) {
-
- if (p_level == p_baker->cell_subdiv - 1) {
-
- Vector3 center = p_aabb.position + p_aabb.size * 0.5;
- Transform xform;
- xform.origin = center;
- xform.basis.scale(p_aabb.size * 0.5);
- p_multimesh->set_instance_transform(idx, xform);
- Color col = Color(p_baker->bake_cells[p_idx].albedo[0], p_baker->bake_cells[p_idx].albedo[1], p_baker->bake_cells[p_idx].albedo[2]);
- //Color col = Color(p_baker->bake_cells[p_idx].emission[0], p_baker->bake_cells[p_idx].emission[1], p_baker->bake_cells[p_idx].emission[2]);
- p_multimesh->set_instance_color(idx, col);
-
- idx++;
-
- } else {
-
- for (int i = 0; i < 8; i++) {
-
- if (p_baker->bake_cells[p_idx].childs[i] == Baker::CHILD_EMPTY)
- continue;
-
- AABB aabb = p_aabb;
- aabb.size *= 0.5;
-
- if (i & 1)
- aabb.position.x += aabb.size.x;
- if (i & 2)
- aabb.position.y += aabb.size.y;
- if (i & 4)
- aabb.position.z += aabb.size.z;
-
- _debug_mesh(p_baker->bake_cells[p_idx].childs[i], p_level + 1, aabb, p_multimesh, idx, p_baker);
- }
- }
-}
-
-void GIProbe::_create_debug_mesh(Baker *p_baker) {
-
- Ref<MultiMesh> mm;
- mm.instance();
-
- mm->set_transform_format(MultiMesh::TRANSFORM_3D);
- mm->set_color_format(MultiMesh::COLOR_8BIT);
- print_line("leaf voxels: " + itos(p_baker->leaf_voxel_count));
- mm->set_instance_count(p_baker->leaf_voxel_count);
-
- Ref<ArrayMesh> mesh;
- mesh.instance();
-
- {
- Array arr;
- arr.resize(Mesh::ARRAY_MAX);
-
- PoolVector<Vector3> vertices;
- PoolVector<Color> colors;
-
- int vtx_idx = 0;
-#define ADD_VTX(m_idx) \
- ; \
- vertices.push_back(face_points[m_idx]); \
- colors.push_back(Color(1, 1, 1, 1)); \
- vtx_idx++;
-
- for (int i = 0; i < 6; i++) {
-
- Vector3 face_points[4];
-
- for (int j = 0; j < 4; j++) {
-
- float v[3];
- v[0] = 1.0;
- v[1] = 1 - 2 * ((j >> 1) & 1);
- v[2] = v[1] * (1 - 2 * (j & 1));
-
- for (int k = 0; k < 3; k++) {
-
- if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
- else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
- }
- }
-
- //tri 1
- ADD_VTX(0);
- ADD_VTX(1);
- ADD_VTX(2);
- //tri 2
- ADD_VTX(2);
- ADD_VTX(3);
- ADD_VTX(0);
- }
-
- arr[Mesh::ARRAY_VERTEX] = vertices;
- arr[Mesh::ARRAY_COLOR] = colors;
- mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr);
- }
-
- {
- Ref<SpatialMaterial> fsm;
- fsm.instance();
- fsm->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
- fsm->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- fsm->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
- fsm->set_albedo(Color(1, 1, 1, 1));
-
- mesh->surface_set_material(0, fsm);
- }
-
- mm->set_mesh(mesh);
-
- int idx = 0;
- _debug_mesh(0, 0, p_baker->po2_bounds, mm, idx, p_baker);
-
- MultiMeshInstance *mmi = memnew(MultiMeshInstance);
- mmi->set_multimesh(mm);
- add_child(mmi);
-#ifdef TOOLS_ENABLED
- if (get_tree()->get_edited_scene_root() == this) {
- mmi->set_owner(this);
- } else {
- mmi->set_owner(get_owner());
- }
-#else
- mmi->set_owner(get_owner());
-#endif
-}
-
void GIProbe::_debug_bake() {
bake(NULL, true);
@@ -1513,8 +552,6 @@ GIProbe::GIProbe() {
normal_bias = 0.0;
propagation = 0.7;
extents = Vector3(10, 10, 10);
- color_scan_cell_width = 4;
- bake_texture_size = 128;
interior = false;
compress = false;
diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h
index 324ff8e917..0858af0001 100644
--- a/scene/3d/gi_probe.h
+++ b/scene/3d/gi_probe.h
@@ -100,67 +100,6 @@ public:
typedef void (*BakeEndFunc)();
private:
- //stuff used for bake
- struct Baker {
-
- enum {
- CHILD_EMPTY = 0xFFFFFFFF
- };
- struct Cell {
-
- uint32_t childs[8];
- float albedo[3]; //albedo in RGB24
- float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
- float normal[3];
- uint32_t used_sides;
- float alpha; //used for upsampling
- int level;
-
- Cell() {
- for (int i = 0; i < 8; i++) {
- childs[i] = CHILD_EMPTY;
- }
-
- for (int i = 0; i < 3; i++) {
- emission[i] = 0;
- albedo[i] = 0;
- normal[i] = 0;
- }
- alpha = 0;
- used_sides = 0;
- level = 0;
- }
- };
-
- Vector<Cell> bake_cells;
- int cell_subdiv;
-
- struct MaterialCache {
- //128x128 textures
- Vector<Color> albedo;
- Vector<Color> emission;
- };
-
- Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color);
- Map<Ref<Material>, MaterialCache> material_cache;
- MaterialCache _get_material_cache(Ref<Material> p_material);
- int leaf_voxel_count;
-
- AABB po2_bounds;
- int axis_cell_size[3];
-
- struct PlotMesh {
- Ref<Material> override_material;
- Vector<Ref<Material> > instance_materials;
- Ref<Mesh> mesh;
- Transform local_xform;
- };
-
- Transform to_cell_space;
-
- List<PlotMesh> mesh_list;
- };
-
Ref<GIProbeData> probe_data;
RID gi_probe;
@@ -175,19 +114,14 @@ private:
bool interior;
bool compress;
- int color_scan_cell_width;
- int bake_texture_size;
-
- Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
- Baker::MaterialCache _get_material_cache(Ref<Material> p_material, Baker *p_baker);
- void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const AABB &p_aabb, Baker *p_baker);
- void _plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
- void _find_meshes(Node *p_at_node, Baker *p_baker);
- void _fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker);
-
- void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, Baker *p_baker);
- void _create_debug_mesh(Baker *p_baker);
+ struct PlotMesh {
+ Ref<Material> override_material;
+ Vector<Ref<Material> > instance_materials;
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ };
+ void _find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes);
void _debug_bake();
protected:
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 126c07f0be..6eb2028d8e 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -142,6 +142,14 @@ PoolVector<Face3> Light::get_faces(uint32_t p_usage_flags) const {
return PoolVector<Face3>();
}
+void Light::set_bake_mode(BakeMode p_mode) {
+ bake_mode = p_mode;
+}
+
+Light::BakeMode Light::get_bake_mode() const {
+ return bake_mode;
+}
+
void Light::_update_visibility() {
if (!is_inside_tree())
@@ -219,12 +227,16 @@ void Light::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light::set_shadow_color);
ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light::get_shadow_color);
+ ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light::set_bake_mode);
+ ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light::get_bake_mode);
+
ADD_GROUP("Light", "light_");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_ENERGY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disable,Indirect,All"), "set_bake_mode", "get_bake_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
@@ -252,6 +264,10 @@ void Light::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS);
BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS_SPLIT_SCALE);
BIND_ENUM_CONSTANT(PARAM_MAX);
+
+ BIND_ENUM_CONSTANT(BAKE_DISABLED);
+ BIND_ENUM_CONSTANT(BAKE_INDIRECT);
+ BIND_ENUM_CONSTANT(BAKE_ALL);
}
Light::Light(VisualServer::LightType p_type) {
@@ -267,6 +283,7 @@ Light::Light(VisualServer::LightType p_type) {
VS::get_singleton()->instance_set_base(get_instance(), light);
reverse_cull = false;
+ bake_mode = BAKE_INDIRECT;
editor_only = false;
set_color(Color(1, 1, 1, 1));
@@ -367,8 +384,8 @@ void DirectionalLight::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_DEPTH_RANGE_OPTIMIZED);
}
-DirectionalLight::DirectionalLight()
- : Light(VisualServer::LIGHT_DIRECTIONAL) {
+DirectionalLight::DirectionalLight() :
+ Light(VisualServer::LIGHT_DIRECTIONAL) {
set_param(PARAM_SHADOW_NORMAL_BIAS, 0.8);
set_param(PARAM_SHADOW_BIAS, 0.1);
@@ -422,8 +439,8 @@ void OmniLight::_bind_methods() {
BIND_ENUM_CONSTANT(SHADOW_DETAIL_HORIZONTAL);
}
-OmniLight::OmniLight()
- : Light(VisualServer::LIGHT_OMNI) {
+OmniLight::OmniLight() :
+ Light(VisualServer::LIGHT_OMNI) {
set_shadow_mode(SHADOW_CUBE);
set_shadow_detail(SHADOW_DETAIL_HORIZONTAL);
diff --git a/scene/3d/light.h b/scene/3d/light.h
index 8514b429ec..7ba25731d9 100644
--- a/scene/3d/light.h
+++ b/scene/3d/light.h
@@ -63,6 +63,12 @@ public:
PARAM_MAX = VS::LIGHT_PARAM_MAX
};
+ enum BakeMode {
+ BAKE_DISABLED,
+ BAKE_INDIRECT,
+ BAKE_ALL
+ };
+
private:
Color color;
float param[PARAM_MAX];
@@ -74,6 +80,7 @@ private:
VS::LightType type;
bool editor_only;
void _update_visibility();
+ BakeMode bake_mode;
// bind helpers
@@ -114,6 +121,9 @@ public:
void set_shadow_reverse_cull_face(bool p_enable);
bool get_shadow_reverse_cull_face() const;
+ void set_bake_mode(BakeMode p_mode);
+ BakeMode get_bake_mode() const;
+
virtual AABB get_aabb() const;
virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
@@ -122,6 +132,7 @@ public:
};
VARIANT_ENUM_CAST(Light::Param);
+VARIANT_ENUM_CAST(Light::BakeMode);
class DirectionalLight : public Light {
@@ -208,8 +219,8 @@ protected:
static void _bind_methods();
public:
- SpotLight()
- : Light(VisualServer::LIGHT_SPOT) {}
+ SpotLight() :
+ Light(VisualServer::LIGHT_SPOT) {}
};
#endif
diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp
index b6507aedb3..78cf75e3b3 100644
--- a/scene/3d/navigation.cpp
+++ b/scene/3d/navigation.cpp
@@ -202,7 +202,7 @@ void Navigation::_navmesh_unlink(int p_id) {
nm.linked = false;
}
-int Navigation::navmesh_create(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) {
+int Navigation::navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner) {
int id = last_id++;
NavMesh nm;
@@ -686,7 +686,7 @@ Vector3 Navigation::get_up_vector() const {
void Navigation::_bind_methods() {
- ClassDB::bind_method(D_METHOD("navmesh_create", "mesh", "xform", "owner"), &Navigation::navmesh_create, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("navmesh_add", "mesh", "xform", "owner"), &Navigation::navmesh_add, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("navmesh_set_transform", "id", "xform"), &Navigation::navmesh_set_transform);
ClassDB::bind_method(D_METHOD("navmesh_remove", "id"), &Navigation::navmesh_remove);
diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h
index 010d16dedd..134afa2278 100644
--- a/scene/3d/navigation.h
+++ b/scene/3d/navigation.h
@@ -58,9 +58,9 @@ class Navigation : public Spatial {
return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
};
- EdgeKey(const Point &p_a = Point(), const Point &p_b = Point())
- : a(p_a),
- b(p_b) {
+ EdgeKey(const Point &p_a = Point(), const Point &p_b = Point()) :
+ a(p_a),
+ b(p_b) {
if (a.key > b.key) {
SWAP(a, b);
}
@@ -166,7 +166,7 @@ public:
Vector3 get_up_vector() const;
//API should be as dynamic as possible
- int navmesh_create(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = NULL);
+ int navmesh_add(const Ref<NavigationMesh> &p_mesh, const Transform &p_xform, Object *p_owner = NULL);
void navmesh_set_transform(int p_id, const Transform &p_xform);
void navmesh_remove(int p_id);
diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp
index 40750cdfe8..4fb12b8fac 100644
--- a/scene/3d/navigation_mesh.cpp
+++ b/scene/3d/navigation_mesh.cpp
@@ -471,7 +471,7 @@ void NavigationMeshInstance::set_enabled(bool p_enabled) {
if (navmesh.is_valid()) {
- nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this);
+ nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
}
}
}
@@ -508,7 +508,7 @@ void NavigationMeshInstance::_notification(int p_what) {
if (enabled && navmesh.is_valid()) {
- nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this);
+ nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
}
break;
}
@@ -568,7 +568,7 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh> &p_na
navmesh = p_navmesh;
if (navigation && navmesh.is_valid() && enabled) {
- nav_id = navigation->navmesh_create(navmesh, get_relative_transform(navigation), this);
+ nav_id = navigation->navmesh_add(navmesh, get_relative_transform(navigation), this);
}
if (debug_view && navmesh.is_valid()) {
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 2a032f5d96..b445ccc5a9 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -598,6 +598,11 @@ void ParticlesMaterial::_update_shader() {
code += "}\n";
code += "\n";
+ code += "float rand_from_seed_m1_p1(inout uint seed) {\n";
+ code += " return rand_from_seed(seed)*2.0-1.0;\n";
+ code += "}\n";
+ code += "\n";
+
//improve seed quality
code += "uint hash(uint x) {\n";
code += " x = ((x >> uint(16)) ^ x) * uint(73244475);\n";
@@ -614,6 +619,8 @@ void ParticlesMaterial::_update_shader() {
code += " float scale_rand = rand_from_seed(alt_seed);\n";
code += " float hue_rot_rand = rand_from_seed(alt_seed);\n";
code += " float anim_offset_rand = rand_from_seed(alt_seed);\n";
+ code += " float pi = 3.14159;\n";
+ code += " float degree_to_rad = pi / 180.0;\n";
code += "\n";
if (emission_shape >= EMISSION_SHAPE_POINTS) {
@@ -638,23 +645,28 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_anim_offset = 0.0;\n";
+ code += " float spread_rad = spread*degree_to_rad;\n";
+
if (flags[FLAG_DISABLE_Z]) {
- code += " float angle1 = (rand_from_seed(alt_seed)*2.0-1.0)*spread/180.0*3.1416;\n";
- code += " vec3 rot = vec3( cos(angle1), sin(angle1),0.0 );\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n";
+ code += " vec3 rot = vec3( cos(angle1_rad), sin(angle1_rad),0.0 );\n";
code += " VELOCITY = rot*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
} else {
//initiate velocity spread in 3D
- code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
- code += " float angle2 = rand_from_seed(alt_seed)*20.0*3.1416; // make it more random like\n";
- code += " vec3 rot_xz = vec3( sin(angle1), 0.0, cos(angle1) );\n";
- code += " vec3 rot = vec3( cos(angle2)*rot_xz.x,sin(angle2)*rot_xz.x, rot_xz.z);\n";
- code += " VELOCITY = rot*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
+ code += " float angle1_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad;\n";
+ code += " float angle2_rad = rand_from_seed_m1_p1(alt_seed)*spread_rad*(1.0-flatness);\n";
+ code += " vec3 direction_xz = vec3( sin(angle1_rad), 0, cos(angle1_rad));\n";
+ code += " vec3 direction_yz = vec3( 0, sin(angle2_rad), cos(angle2_rad));\n";
+ code += " direction_yz.z = direction_yz.z / sqrt(direction_yz.z); //better uniform distribution\n";
+ code += " vec3 direction = vec3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z);\n";
+ code += " direction = normalize(direction);\n";
+ code += " VELOCITY = direction*initial_linear_velocity*mix(1.0, rand_from_seed(alt_seed), initial_linear_velocity_random);\n";
}
code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
- code += " CUSTOM.x = base_angle*3.1416/180.0;\n"; //angle
+ code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle
code += " CUSTOM.y = 0.0;\n"; //phase
code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random);\n"; //animation offset (0-1)
switch (emission_shape) {
@@ -703,10 +715,13 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_linear_velocity = 0.0;\n";
- if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid())
- code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n";
- else
- code += " float tex_orbit_velocity = 0.0;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ if (tex_parameters[PARAM_ORBIT_VELOCITY].is_valid())
+ code += " float tex_orbit_velocity = textureLod(orbit_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n";
+ else
+ code += " float tex_orbit_velocity = 0.0;\n";
+ }
if (tex_parameters[PARAM_ANGULAR_VELOCITY].is_valid())
code += " float tex_angular_velocity = textureLod(angular_velocity_texture,vec2(CUSTOM.y,0.0),0.0).r;\n";
@@ -756,7 +771,7 @@ void ParticlesMaterial::_update_shader() {
code += " //apply linear acceleration\n";
code += " force += length(VELOCITY) > 0.0 ? normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random) : vec3(0.0);\n";
code += " //apply radial acceleration\n";
- code += " vec3 org = vec3(0.0);\n";
+ code += " vec3 org = EMISSION_TRANSFORM[3].xyz;\n";
code += " vec3 diff = pos-org;\n";
code += " force += length(diff) > 0.0 ? normalize(diff) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random) : vec3(0.0);\n";
code += " //apply tangential acceleration;\n";
@@ -769,6 +784,18 @@ void ParticlesMaterial::_update_shader() {
}
code += " //apply attractor forces\n";
code += " VELOCITY += force * DELTA;\n";
+ code += " //orbit velocity\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " float orbit_amount = (orbit_velocity+tex_orbit_velocity)*mix(1.0,rand_from_seed(alt_seed),orbit_velocity_random);\n";
+ code += " if (orbit_amount!=0.0) {\n";
+ code += " float ang = orbit_amount * DELTA * pi * 2.0;\n";
+ code += " mat2 rot = mat2(vec2(cos(ang),-sin(ang)),vec2(sin(ang),cos(ang)));\n";
+ code += " TRANSFORM[3].xy-=diff.xy;\n";
+ code += " TRANSFORM[3].xy+=rot * diff.xy;\n";
+ code += " }\n";
+ }
+
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " VELOCITY = normalize(VELOCITY)*tex_linear_velocity;\n";
}
@@ -785,7 +812,7 @@ void ParticlesMaterial::_update_shader() {
code += " }\n";
code += " float base_angle = (initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random);\n";
code += " base_angle += CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random);\n";
- code += " CUSTOM.x = base_angle*3.1416/180.0;\n"; //angle
+ code += " CUSTOM.x = base_angle*degree_to_rad;\n"; //angle
code += " CUSTOM.z = (anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
if (flags[FLAG_ANIM_LOOP]) {
code += " CUSTOM.z = mod(CUSTOM.z,1.0);\n"; //loop
@@ -806,7 +833,7 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_hue_variation = 0.0;\n";
- code += " float hue_rot_angle = (hue_variation+tex_hue_variation)*3.1416*2.0*mix(1.0,hue_rot_rand*2.0-1.0,hue_variation_random);\n";
+ code += " float hue_rot_angle = (hue_variation+tex_hue_variation)*pi*2.0*mix(1.0,hue_rot_rand*2.0-1.0,hue_variation_random);\n";
code += " float hue_rot_c = cos(hue_rot_angle);\n";
code += " float hue_rot_s = sin(hue_rot_angle);\n";
code += " mat4 hue_rot_mat = mat4( vec4(0.299, 0.587, 0.114, 0.0),\n";
@@ -869,6 +896,7 @@ void ParticlesMaterial::_update_shader() {
}
//scale by scale
code += " float base_scale = mix(scale*tex_scale,1.0,scale_random*scale_rand);\n";
+ code += " if (base_scale==0.0) base_scale=0.000001;\n";
if (trail_size_modifier.is_valid()) {
code += " if (trail_divisor > 1) { base_scale *= textureLod(trail_size_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0).r; } \n";
}
@@ -1173,6 +1201,9 @@ void ParticlesMaterial::set_flag(Flags p_flag, bool p_enable) {
ERR_FAIL_INDEX(p_flag, FLAG_MAX);
flags[p_flag] = p_enable;
_queue_shader_change();
+ if (p_flag == FLAG_DISABLE_Z) {
+ _change_notify();
+ }
}
bool ParticlesMaterial::get_flag(Flags p_flag) const {
@@ -1358,6 +1389,15 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
if (property.name == "emission_point_count" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
property.usage = 0;
}
+
+ if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) {
+ property.usage = 0;
+ }
+}
+
+Shader::Mode ParticlesMaterial::get_shader_mode() const {
+
+ return Shader::MODE_PARTICLES;
}
void ParticlesMaterial::_bind_methods() {
@@ -1517,8 +1557,8 @@ void ParticlesMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS);
}
-ParticlesMaterial::ParticlesMaterial()
- : element(this) {
+ParticlesMaterial::ParticlesMaterial() :
+ element(this) {
set_spread(45);
set_flatness(0);
diff --git a/scene/3d/particles.h b/scene/3d/particles.h
index 30080360bb..5b8121e937 100644
--- a/scene/3d/particles.h
+++ b/scene/3d/particles.h
@@ -390,6 +390,8 @@ public:
RID get_shader_rid() const;
+ virtual Shader::Mode get_shader_mode() const;
+
ParticlesMaterial();
~ParticlesMaterial();
};
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 4e06b272e2..c5f817d317 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -166,8 +166,8 @@ void PhysicsBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
}
-PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode)
- : CollisionObject(PhysicsServer::get_singleton()->body_create(p_mode), false) {
+PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) :
+ CollisionObject(PhysicsServer::get_singleton()->body_create(p_mode), false) {
collision_layer = 1;
collision_mask = 1;
@@ -241,8 +241,8 @@ void StaticBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity");
}
-StaticBody::StaticBody()
- : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) {
+StaticBody::StaticBody() :
+ PhysicsBody(PhysicsServer::BODY_MODE_STATIC) {
bounce = 0;
friction = 1;
@@ -734,15 +734,12 @@ bool RigidBody::is_contact_monitor_enabled() const {
return contact_monitor != NULL;
}
-void RigidBody::set_axis_lock(AxisLock p_lock) {
-
- axis_lock = p_lock;
- PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), PhysicsServer::BodyAxisLock(axis_lock));
+void RigidBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) {
+ PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock);
}
-RigidBody::AxisLock RigidBody::get_axis_lock() const {
-
- return axis_lock;
+bool RigidBody::get_axis_lock(PhysicsServer::BodyAxis p_axis) const {
+ return PhysicsServer::get_singleton()->body_is_axis_locked(get_rid(), p_axis);
}
Array RigidBody::get_colliding_bodies() const {
@@ -837,8 +834,8 @@ void RigidBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("_body_enter_tree"), &RigidBody::_body_enter_tree);
ClassDB::bind_method(D_METHOD("_body_exit_tree"), &RigidBody::_body_exit_tree);
- ClassDB::bind_method(D_METHOD("set_axis_lock", "axis_lock"), &RigidBody::set_axis_lock);
- ClassDB::bind_method(D_METHOD("get_axis_lock"), &RigidBody::get_axis_lock);
+ ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &RigidBody::set_axis_lock);
+ ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &RigidBody::get_axis_lock);
ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody::get_colliding_bodies);
@@ -856,7 +853,13 @@ void RigidBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "axis_lock", PROPERTY_HINT_ENUM, "Disabled,Lock X,Lock Y,Lock Z"), "set_axis_lock", "get_axis_lock");
+ ADD_GROUP("Axis Lock", "axis_lock_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Z);
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "-1,128,0.01"), "set_linear_damp", "get_linear_damp");
@@ -874,15 +877,10 @@ void RigidBody::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_STATIC);
BIND_ENUM_CONSTANT(MODE_CHARACTER);
BIND_ENUM_CONSTANT(MODE_KINEMATIC);
-
- BIND_ENUM_CONSTANT(AXIS_LOCK_DISABLED);
- BIND_ENUM_CONSTANT(AXIS_LOCK_X);
- BIND_ENUM_CONSTANT(AXIS_LOCK_Y);
- BIND_ENUM_CONSTANT(AXIS_LOCK_Z);
}
-RigidBody::RigidBody()
- : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
+RigidBody::RigidBody() :
+ PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
mode = MODE_RIGID;
@@ -904,8 +902,6 @@ RigidBody::RigidBody()
contact_monitor = NULL;
can_sleep = true;
- axis_lock = AXIS_LOCK_DISABLED;
-
PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
}
@@ -952,6 +948,12 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, Collision &r_colli
r_collision.local_shape = result.collision_local_shape;
}
+ for (int i = 0; i < 3; i++) {
+ if (locked_axis & (1 << i)) {
+ result.motion[i] = 0;
+ }
+ }
+
gt.origin += result.motion;
set_global_transform(gt);
@@ -960,9 +962,16 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, Collision &r_colli
Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) {
- Vector3 motion = (floor_velocity + p_linear_velocity) * get_physics_process_delta_time();
Vector3 lv = p_linear_velocity;
+ for (int i = 0; i < 3; i++) {
+ if (locked_axis & (1 << i)) {
+ lv[i] = 0;
+ }
+ }
+
+ Vector3 motion = (floor_velocity + lv) * get_physics_process_delta_time();
+
on_floor = false;
on_ceiling = false;
on_wall = false;
@@ -1008,6 +1017,12 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve
motion = motion.slide(n);
lv = lv.slide(n);
+ for (int i = 0; i < 3; i++) {
+ if (locked_axis & (1 << i)) {
+ lv[i] = 0;
+ }
+ }
+
colliders.push_back(collision);
} else {
@@ -1047,6 +1062,14 @@ bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion)
return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion);
}
+void KinematicBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) {
+ PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock);
+}
+
+bool KinematicBody::get_axis_lock(PhysicsServer::BodyAxis p_axis) const {
+ return PhysicsServer::get_singleton()->body_is_axis_locked(get_rid(), p_axis);
+}
+
void KinematicBody::set_safe_margin(float p_margin) {
margin = p_margin;
@@ -1095,20 +1118,31 @@ void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody::is_on_wall);
ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody::get_floor_velocity);
+ ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &KinematicBody::set_axis_lock);
+ ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &KinematicBody::get_axis_lock);
+
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &KinematicBody::get_safe_margin);
ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody::get_slide_count);
ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody::_get_slide_collision);
+ ADD_GROUP("Axis Lock", "axis_lock_");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_X);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Z);
+
ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
-KinematicBody::KinematicBody()
- : PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC) {
+KinematicBody::KinematicBody() :
+ PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC) {
margin = 0.001;
-
+ locked_axis = 0;
on_floor = false;
on_ceiling = false;
on_wall = false;
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index f88b3860dc..9d9feda0b2 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -114,13 +114,6 @@ public:
MODE_KINEMATIC,
};
- enum AxisLock {
- AXIS_LOCK_DISABLED,
- AXIS_LOCK_X,
- AXIS_LOCK_Y,
- AXIS_LOCK_Z,
- };
-
private:
bool can_sleep;
PhysicsDirectBodyState *state;
@@ -139,8 +132,6 @@ private:
bool sleeping;
bool ccd;
- AxisLock axis_lock;
-
int max_contacts_reported;
bool custom_integrator;
@@ -245,8 +236,8 @@ public:
void set_use_continuous_collision_detection(bool p_enable);
bool is_using_continuous_collision_detection() const;
- void set_axis_lock(AxisLock p_lock);
- AxisLock get_axis_lock() const;
+ void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock);
+ bool get_axis_lock(PhysicsServer::BodyAxis p_axis) const;
Array get_colliding_bodies() const;
@@ -259,7 +250,6 @@ public:
};
VARIANT_ENUM_CAST(RigidBody::Mode);
-VARIANT_ENUM_CAST(RigidBody::AxisLock);
class KinematicCollision;
@@ -281,6 +271,8 @@ public:
};
private:
+ uint16_t locked_axis;
+
float margin;
Vector3 floor_velocity;
@@ -303,6 +295,9 @@ public:
bool move_and_collide(const Vector3 &p_motion, Collision &r_collision);
bool test_move(const Transform &p_from, const Vector3 &p_motion);
+ void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock);
+ bool get_axis_lock(PhysicsServer::BodyAxis p_axis) const;
+
void set_safe_margin(float p_margin);
float get_safe_margin() const;
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index 588aa2881a..d9f88ac693 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -756,8 +756,8 @@ void Spatial::_bind_methods() {
ADD_SIGNAL(MethodInfo("visibility_changed"));
}
-Spatial::Spatial()
- : xform_change(this) {
+Spatial::Spatial() :
+ xform_change(this) {
data.dirty = DIRTY_NONE;
data.children_lock = 0;
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index a072572142..2e3e179a7b 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -54,8 +54,8 @@ public:
const Vector3 &inertiaInvA,
const real_t massInvA,
const Vector3 &inertiaInvB,
- const real_t massInvB)
- : m_linearJointAxis(jointAxis) {
+ const real_t massInvB) :
+ m_linearJointAxis(jointAxis) {
m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis));
m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis));
m_0MinvJt = inertiaInvA * m_aJ;
@@ -593,12 +593,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec
#endif
}
-VehicleBody::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse)
- : m_s(s),
- m_body1(body1),
- m_frictionPositionWorld(frictionPosWorld),
- m_frictionDirectionWorld(frictionDirectionWorld),
- m_maxImpulse(maxImpulse) {
+VehicleBody::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse) :
+ m_s(s),
+ m_body1(body1),
+ m_frictionPositionWorld(frictionPosWorld),
+ m_frictionDirectionWorld(frictionDirectionWorld),
+ m_maxImpulse(maxImpulse) {
float denom0 = 0;
float denom1 = 0;
@@ -969,8 +969,8 @@ void VehicleBody::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_friction", "get_friction");
}
-VehicleBody::VehicleBody()
- : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
+VehicleBody::VehicleBody() :
+ PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
m_pitchControl = 0;
m_currentVehicleSpeedKmHour = real_t(0.);
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
new file mode 100644
index 0000000000..98dc1590d8
--- /dev/null
+++ b/scene/3d/voxel_light_baker.cpp
@@ -0,0 +1,2373 @@
+#include "voxel_light_baker.h"
+#include "os/os.h"
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if (x1 < min) min = x1; \
+ if (x1 > max) max = x1; \
+ if (x2 < min) min = x2; \
+ if (x2 > max) max = x2;
+
+static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
+ int q;
+ Vector3 vmin, vmax;
+ for (q = 0; q <= 2; q++) {
+ if (normal[q] > 0.0f) {
+ vmin[q] = -maxbox[q];
+ vmax[q] = maxbox[q];
+ } else {
+ vmin[q] = maxbox[q];
+ vmax[q] = -maxbox[q];
+ }
+ }
+ if (normal.dot(vmin) + d > 0.0f) return false;
+ if (normal.dot(vmax) + d >= 0.0f) return true;
+
+ return false;
+}
+
+/*======================== X-tests ========================*/
+#define AXISTEST_X01(a, b, fa, fb) \
+ p0 = a * v0.y - b * v0.z; \
+ p2 = a * v2.y - b * v2.z; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_X2(a, b, fa, fb) \
+ p0 = a * v0.y - b * v0.z; \
+ p1 = a * v1.y - b * v1.z; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.y + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+/*======================== Y-tests ========================*/
+#define AXISTEST_Y02(a, b, fa, fb) \
+ p0 = -a * v0.x + b * v0.z; \
+ p2 = -a * v2.x + b * v2.z; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_Y1(a, b, fa, fb) \
+ p0 = -a * v0.x + b * v0.z; \
+ p1 = -a * v1.x + b * v1.z; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.z; \
+ if (min > rad || max < -rad) return false;
+
+ /*======================== Z-tests ========================*/
+
+#define AXISTEST_Z12(a, b, fa, fb) \
+ p1 = a * v1.x - b * v1.y; \
+ p2 = a * v2.x - b * v2.y; \
+ if (p2 < p1) { \
+ min = p2; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p2; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+ if (min > rad || max < -rad) return false;
+
+#define AXISTEST_Z0(a, b, fa, fb) \
+ p0 = a * v0.x - b * v0.y; \
+ p1 = a * v1.x - b * v1.y; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize.x + fb * boxhalfsize.y; \
+ if (min > rad || max < -rad) return false;
+
+static bool fast_tri_box_overlap(const Vector3 &boxcenter, const Vector3 boxhalfsize, const Vector3 *triverts) {
+
+ /* use separating axis theorem to test overlap between triangle and box */
+ /* need to test for overlap in these directions: */
+ /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */
+ /* we do not even need to test these) */
+ /* 2) normal of the triangle */
+ /* 3) crossproduct(edge from tri, {x,y,z}-directin) */
+ /* this gives 3x3=9 more tests */
+ Vector3 v0, v1, v2;
+ float min, max, d, p0, p1, p2, rad, fex, fey, fez;
+ Vector3 normal, e0, e1, e2;
+
+ /* This is the fastest branch on Sun */
+ /* move everything so that the boxcenter is in (0,0,0) */
+
+ v0 = triverts[0] - boxcenter;
+ v1 = triverts[1] - boxcenter;
+ v2 = triverts[2] - boxcenter;
+
+ /* compute triangle edges */
+ e0 = v1 - v0; /* tri edge 0 */
+ e1 = v2 - v1; /* tri edge 1 */
+ e2 = v0 - v2; /* tri edge 2 */
+
+ /* Bullet 3: */
+ /* test the 9 tests first (this was faster) */
+ fex = Math::abs(e0.x);
+ fey = Math::abs(e0.y);
+ fez = Math::abs(e0.z);
+ AXISTEST_X01(e0.z, e0.y, fez, fey);
+ AXISTEST_Y02(e0.z, e0.x, fez, fex);
+ AXISTEST_Z12(e0.y, e0.x, fey, fex);
+
+ fex = Math::abs(e1.x);
+ fey = Math::abs(e1.y);
+ fez = Math::abs(e1.z);
+ AXISTEST_X01(e1.z, e1.y, fez, fey);
+ AXISTEST_Y02(e1.z, e1.x, fez, fex);
+ AXISTEST_Z0(e1.y, e1.x, fey, fex);
+
+ fex = Math::abs(e2.x);
+ fey = Math::abs(e2.y);
+ fez = Math::abs(e2.z);
+ AXISTEST_X2(e2.z, e2.y, fez, fey);
+ AXISTEST_Y1(e2.z, e2.x, fez, fex);
+ AXISTEST_Z12(e2.y, e2.x, fey, fex);
+
+ /* Bullet 1: */
+ /* first test overlap in the {x,y,z}-directions */
+ /* find min, max of the triangle each direction, and test for overlap in */
+ /* that direction -- this is equivalent to testing a minimal AABB around */
+ /* the triangle against the AABB */
+
+ /* test in X-direction */
+ FINDMINMAX(v0.x, v1.x, v2.x, min, max);
+ if (min > boxhalfsize.x || max < -boxhalfsize.x) return false;
+
+ /* test in Y-direction */
+ FINDMINMAX(v0.y, v1.y, v2.y, min, max);
+ if (min > boxhalfsize.y || max < -boxhalfsize.y) return false;
+
+ /* test in Z-direction */
+ FINDMINMAX(v0.z, v1.z, v2.z, min, max);
+ if (min > boxhalfsize.z || max < -boxhalfsize.z) return false;
+
+ /* Bullet 2: */
+ /* test if the box intersects the plane of the triangle */
+ /* compute plane equation of triangle: normal*x+d=0 */
+ normal = e0.cross(e1);
+ d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
+ if (!planeBoxOverlap(normal, d, boxhalfsize)) return false;
+
+ return true; /* box and triangle overlaps */
+}
+
+static _FORCE_INLINE_ Vector2 get_uv(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv) {
+
+ if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2)
+ return p_uv[0];
+ if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2)
+ return p_uv[1];
+ if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2)
+ return p_uv[2];
+
+ Vector3 v0 = p_vtx[1] - p_vtx[0];
+ Vector3 v1 = p_vtx[2] - p_vtx[0];
+ Vector3 v2 = p_pos - p_vtx[0];
+
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom == 0)
+ return p_uv[0];
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
+}
+
+void VoxelLightBaker::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb) {
+
+ if (p_level == cell_subdiv - 1) {
+ //plot the face by guessing it's albedo and emission value
+
+ //find best axis to map to, for scanning values
+ int closest_axis = 0;
+ float closest_dot = 0;
+
+ Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]);
+ Vector3 normal = plane.normal;
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 axis;
+ axis[i] = 1.0;
+ float dot = ABS(normal.dot(axis));
+ if (i == 0 || dot > closest_dot) {
+ closest_axis = i;
+ closest_dot = dot;
+ }
+ }
+
+ Vector3 axis;
+ axis[closest_axis] = 1.0;
+ Vector3 t1;
+ t1[(closest_axis + 1) % 3] = 1.0;
+ Vector3 t2;
+ t2[(closest_axis + 2) % 3] = 1.0;
+
+ t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width);
+ t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width);
+
+ Color albedo_accum;
+ Color emission_accum;
+ Vector3 normal_accum;
+
+ float alpha = 0.0;
+
+ //map to a grid average in the best axis for this face
+ for (int i = 0; i < color_scan_cell_width; i++) {
+
+ Vector3 ofs_i = float(i) * t1;
+
+ for (int j = 0; j < color_scan_cell_width; j++) {
+
+ Vector3 ofs_j = float(j) * t2;
+
+ Vector3 from = p_aabb.position + ofs_i + ofs_j;
+ Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis];
+ Vector3 half = (to - from) * 0.5;
+
+ //is in this cell?
+ if (!fast_tri_box_overlap(from + half, half, p_vtx)) {
+ continue; //face does not span this cell
+ }
+
+ //go from -size to +size*2 to avoid skipping collisions
+ Vector3 ray_from = from + (t1 + t2) * 0.5 - axis * p_aabb.size[closest_axis];
+ Vector3 ray_to = ray_from + axis * p_aabb.size[closest_axis] * 2;
+
+ if (normal.dot(ray_from - ray_to) < 0) {
+ SWAP(ray_from, ray_to);
+ }
+
+ Vector3 intersection;
+
+ if (!plane.intersects_segment(ray_from, ray_to, &intersection)) {
+ if (ABS(plane.distance_to(ray_from)) < ABS(plane.distance_to(ray_to))) {
+ intersection = plane.project(ray_from);
+ } else {
+
+ intersection = plane.project(ray_to);
+ }
+ }
+
+ intersection = Face3(p_vtx[0], p_vtx[1], p_vtx[2]).get_closest_point_to(intersection);
+
+ Vector2 uv = get_uv(intersection, p_vtx, p_uv);
+
+ int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+ int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+
+ int ofs = uv_y * bake_texture_size + uv_x;
+ albedo_accum.r += p_material.albedo[ofs].r;
+ albedo_accum.g += p_material.albedo[ofs].g;
+ albedo_accum.b += p_material.albedo[ofs].b;
+ albedo_accum.a += p_material.albedo[ofs].a;
+
+ emission_accum.r += p_material.emission[ofs].r;
+ emission_accum.g += p_material.emission[ofs].g;
+ emission_accum.b += p_material.emission[ofs].b;
+
+ normal_accum += normal;
+
+ alpha += 1.0;
+ }
+ }
+
+ if (alpha == 0) {
+ //could not in any way get texture information.. so use closest point to center
+
+ Face3 f(p_vtx[0], p_vtx[1], p_vtx[2]);
+ Vector3 inters = f.get_closest_point_to(p_aabb.position + p_aabb.size * 0.5);
+
+ Vector2 uv = get_uv(inters, p_vtx, p_uv);
+
+ int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+ int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1);
+
+ int ofs = uv_y * bake_texture_size + uv_x;
+
+ alpha = 1.0 / (color_scan_cell_width * color_scan_cell_width);
+
+ albedo_accum.r = p_material.albedo[ofs].r * alpha;
+ albedo_accum.g = p_material.albedo[ofs].g * alpha;
+ albedo_accum.b = p_material.albedo[ofs].b * alpha;
+ albedo_accum.a = p_material.albedo[ofs].a * alpha;
+
+ emission_accum.r = p_material.emission[ofs].r * alpha;
+ emission_accum.g = p_material.emission[ofs].g * alpha;
+ emission_accum.b = p_material.emission[ofs].b * alpha;
+
+ normal_accum *= alpha;
+
+ } else {
+
+ float accdiv = 1.0 / (color_scan_cell_width * color_scan_cell_width);
+ alpha *= accdiv;
+
+ albedo_accum.r *= accdiv;
+ albedo_accum.g *= accdiv;
+ albedo_accum.b *= accdiv;
+ albedo_accum.a *= accdiv;
+
+ emission_accum.r *= accdiv;
+ emission_accum.g *= accdiv;
+ emission_accum.b *= accdiv;
+
+ normal_accum *= accdiv;
+ }
+
+ //put this temporarily here, corrected in a later step
+ bake_cells[p_idx].albedo[0] += albedo_accum.r;
+ bake_cells[p_idx].albedo[1] += albedo_accum.g;
+ bake_cells[p_idx].albedo[2] += albedo_accum.b;
+ bake_cells[p_idx].emission[0] += emission_accum.r;
+ bake_cells[p_idx].emission[1] += emission_accum.g;
+ bake_cells[p_idx].emission[2] += emission_accum.b;
+ bake_cells[p_idx].normal[0] += normal_accum.x;
+ bake_cells[p_idx].normal[1] += normal_accum.y;
+ bake_cells[p_idx].normal[2] += normal_accum.z;
+ bake_cells[p_idx].alpha += alpha;
+
+ } else {
+ //go down
+
+ int half = (1 << (cell_subdiv - 1)) >> (p_level + 1);
+ for (int i = 0; i < 8; i++) {
+
+ AABB aabb = p_aabb;
+ aabb.size *= 0.5;
+
+ int nx = p_x;
+ int ny = p_y;
+ int nz = p_z;
+
+ if (i & 1) {
+ aabb.position.x += aabb.size.x;
+ nx += half;
+ }
+ if (i & 2) {
+ aabb.position.y += aabb.size.y;
+ ny += half;
+ }
+ if (i & 4) {
+ aabb.position.z += aabb.size.z;
+ nz += half;
+ }
+ //make sure to not plot beyond limits
+ if (nx < 0 || nx >= axis_cell_size[0] || ny < 0 || ny >= axis_cell_size[1] || nz < 0 || nz >= axis_cell_size[2])
+ continue;
+
+ {
+ AABB test_aabb = aabb;
+ //test_aabb.grow_by(test_aabb.get_longest_axis_size()*0.05); //grow a bit to avoid numerical error in real-time
+ Vector3 qsize = test_aabb.size * 0.5; //quarter size, for fast aabb test
+
+ if (!fast_tri_box_overlap(test_aabb.position + qsize, qsize, p_vtx)) {
+ //if (!Face3(p_vtx[0],p_vtx[1],p_vtx[2]).intersects_aabb2(aabb)) {
+ //does not fit in child, go on
+ continue;
+ }
+ }
+
+ if (bake_cells[p_idx].childs[i] == CHILD_EMPTY) {
+ //sub cell must be created
+
+ uint32_t child_idx = bake_cells.size();
+ bake_cells[p_idx].childs[i] = child_idx;
+ bake_cells.resize(bake_cells.size() + 1);
+ bake_cells[child_idx].level = p_level + 1;
+ }
+
+ _plot_face(bake_cells[p_idx].childs[i], p_level + 1, nx, ny, nz, p_vtx, p_uv, p_material, aabb);
+ }
+ }
+}
+
+Vector<Color> VoxelLightBaker::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) {
+
+ Vector<Color> ret;
+
+ if (p_image.is_null() || p_image->empty()) {
+
+ ret.resize(bake_texture_size * bake_texture_size);
+ for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
+ ret[i] = p_color_add;
+ }
+
+ return ret;
+ }
+ p_image = p_image->duplicate();
+
+ if (p_image->is_compressed()) {
+ print_line("DECOMPRESSING!!!!");
+
+ p_image->decompress();
+ }
+ p_image->convert(Image::FORMAT_RGBA8);
+ p_image->resize(bake_texture_size, bake_texture_size, Image::INTERPOLATE_CUBIC);
+
+ PoolVector<uint8_t>::Read r = p_image->get_data().read();
+ ret.resize(bake_texture_size * bake_texture_size);
+
+ for (int i = 0; i < bake_texture_size * bake_texture_size; i++) {
+ Color c;
+ c.r = (r[i * 4 + 0] / 255.0) * p_color_mul.r + p_color_add.r;
+ c.g = (r[i * 4 + 1] / 255.0) * p_color_mul.g + p_color_add.g;
+ c.b = (r[i * 4 + 2] / 255.0) * p_color_mul.b + p_color_add.b;
+
+ c.a = r[i * 4 + 3] / 255.0;
+
+ ret[i] = c;
+ }
+
+ return ret;
+}
+
+VoxelLightBaker::MaterialCache VoxelLightBaker::_get_material_cache(Ref<Material> p_material) {
+
+ //this way of obtaining materials is inaccurate and also does not support some compressed formats very well
+ Ref<SpatialMaterial> mat = p_material;
+
+ Ref<Material> material = mat; //hack for now
+
+ if (material_cache.has(material)) {
+ return material_cache[material];
+ }
+
+ MaterialCache mc;
+
+ if (mat.is_valid()) {
+
+ Ref<Texture> albedo_tex = mat->get_texture(SpatialMaterial::TEXTURE_ALBEDO);
+
+ Ref<Image> img_albedo;
+ if (albedo_tex.is_valid()) {
+
+ img_albedo = albedo_tex->get_data();
+ mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative
+ } else {
+ mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive
+ }
+
+ Ref<Texture> emission_tex = mat->get_texture(SpatialMaterial::TEXTURE_EMISSION);
+
+ Color emission_col = mat->get_emission();
+ float emission_energy = mat->get_emission_energy();
+
+ Ref<Image> img_emission;
+
+ if (emission_tex.is_valid()) {
+
+ img_emission = emission_tex->get_data();
+ }
+
+ if (mat->get_emission_operator() == SpatialMaterial::EMISSION_OP_ADD) {
+ mc.emission = _get_bake_texture(img_emission, Color(1, 1, 1) * emission_energy, emission_col * emission_energy);
+ } else {
+ mc.emission = _get_bake_texture(img_emission, emission_col * emission_energy, Color(0, 0, 0));
+ }
+
+ } else {
+ Ref<Image> empty;
+
+ mc.albedo = _get_bake_texture(empty, Color(0, 0, 0), Color(1, 1, 1));
+ mc.emission = _get_bake_texture(empty, Color(0, 0, 0), Color(0, 0, 0));
+ }
+
+ material_cache[p_material] = mc;
+ return mc;
+}
+
+void VoxelLightBaker::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) {
+
+ for (int i = 0; i < p_mesh->get_surface_count(); i++) {
+
+ if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
+ continue; //only triangles
+
+ Ref<Material> src_material;
+
+ if (p_override_material.is_valid()) {
+ src_material = p_override_material;
+ } else if (i < p_materials.size() && p_materials[i].is_valid()) {
+ src_material = p_materials[i];
+ } else {
+ src_material = p_mesh->surface_get_material(i);
+ }
+ MaterialCache material = _get_material_cache(src_material);
+
+ Array a = p_mesh->surface_get_arrays(i);
+
+ PoolVector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ PoolVector<Vector3>::Read vr = vertices.read();
+ PoolVector<Vector2> uv = a[Mesh::ARRAY_TEX_UV];
+ PoolVector<Vector2>::Read uvr;
+ PoolVector<int> index = a[Mesh::ARRAY_INDEX];
+
+ bool read_uv = false;
+
+ if (uv.size()) {
+
+ uvr = uv.read();
+ read_uv = true;
+ }
+
+ if (index.size()) {
+
+ int facecount = index.size() / 3;
+ PoolVector<int>::Read ir = index.read();
+
+ for (int j = 0; j < facecount; j++) {
+
+ Vector3 vtxs[3];
+ Vector2 uvs[3];
+
+ for (int k = 0; k < 3; k++) {
+ vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]);
+ }
+
+ if (read_uv) {
+ for (int k = 0; k < 3; k++) {
+ uvs[k] = uvr[ir[j * 3 + k]];
+ }
+ }
+
+ //test against original bounds
+ if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+ continue;
+ //plot
+ _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds);
+ }
+
+ } else {
+
+ int facecount = vertices.size() / 3;
+
+ for (int j = 0; j < facecount; j++) {
+
+ Vector3 vtxs[3];
+ Vector2 uvs[3];
+
+ for (int k = 0; k < 3; k++) {
+ vtxs[k] = p_xform.xform(vr[j * 3 + k]);
+ }
+
+ if (read_uv) {
+ for (int k = 0; k < 3; k++) {
+ uvs[k] = uvr[j * 3 + k];
+ }
+ }
+
+ //test against original bounds
+ if (!fast_tri_box_overlap(original_bounds.position + original_bounds.size * 0.5, original_bounds.size * 0.5, vtxs))
+ continue;
+ //plot face
+ _plot_face(0, 0, 0, 0, 0, vtxs, uvs, material, po2_bounds);
+ }
+ }
+ }
+
+ max_original_cells = bake_cells.size();
+}
+
+void VoxelLightBaker::_init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent) {
+
+ bake_light[p_idx].x = p_x;
+ bake_light[p_idx].y = p_y;
+ bake_light[p_idx].z = p_z;
+
+ if (p_level == cell_subdiv - 1) {
+
+ bake_light[p_idx].next_leaf = first_leaf;
+ first_leaf = p_idx;
+ } else {
+
+ //go down
+ int half = (1 << (cell_subdiv - 1)) >> (p_level + 1);
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY)
+ continue;
+
+ int nx = p_x;
+ int ny = p_y;
+ int nz = p_z;
+
+ if (i & 1)
+ nx += half;
+ if (i & 2)
+ ny += half;
+ if (i & 4)
+ nz += half;
+
+ _init_light_plot(child, p_level + 1, nx, ny, nz, p_idx);
+ }
+ }
+}
+
+void VoxelLightBaker::begin_bake_light(BakeQuality p_quality, BakeMode p_bake_mode, float p_propagation, float p_energy) {
+ _check_init_light();
+ propagation = p_propagation;
+ bake_quality = p_quality;
+ bake_mode = p_bake_mode;
+ energy = p_energy;
+}
+
+void VoxelLightBaker::_check_init_light() {
+ if (bake_light.size() == 0) {
+
+ direct_lights_baked = false;
+ leaf_voxel_count = 0;
+ _fixup_plot(0, 0); //pre fixup, so normal, albedo, emission, etc. work for lighting.
+ bake_light.resize(bake_cells.size());
+ zeromem(bake_light.ptrw(), bake_light.size() * sizeof(Light));
+ first_leaf = -1;
+ _init_light_plot(0, 0, 0, 0, 0, CHILD_EMPTY);
+ }
+}
+
+static float _get_normal_advance(const Vector3 &p_normal) {
+
+ Vector3 normal = p_normal;
+ Vector3 unorm = normal.abs();
+
+ if ((unorm.x >= unorm.y) && (unorm.x >= unorm.z)) {
+ // x code
+ unorm = normal.x > 0.0 ? Vector3(1.0, 0.0, 0.0) : Vector3(-1.0, 0.0, 0.0);
+ } else if ((unorm.y > unorm.x) && (unorm.y >= unorm.z)) {
+ // y code
+ unorm = normal.y > 0.0 ? Vector3(0.0, 1.0, 0.0) : Vector3(0.0, -1.0, 0.0);
+ } else if ((unorm.z > unorm.x) && (unorm.z > unorm.y)) {
+ // z code
+ unorm = normal.z > 0.0 ? Vector3(0.0, 0.0, 1.0) : Vector3(0.0, 0.0, -1.0);
+ } else {
+ // oh-no we messed up code
+ // has to be
+ unorm = Vector3(1.0, 0.0, 0.0);
+ }
+
+ return 1.0 / normal.dot(unorm);
+}
+
+static const Vector3 aniso_normal[6] = {
+ Vector3(-1, 0, 0),
+ Vector3(1, 0, 0),
+ Vector3(0, -1, 0),
+ Vector3(0, 1, 0),
+ Vector3(0, 0, -1),
+ Vector3(0, 0, 1)
+};
+
+uint32_t VoxelLightBaker::_find_cell_at_pos(const Cell *cells, int x, int y, int z) {
+
+ uint32_t cell = 0;
+
+ int ofs_x = 0;
+ int ofs_y = 0;
+ int ofs_z = 0;
+ int size = 1 << (cell_subdiv - 1);
+ int half = size / 2;
+
+ if (x < 0 || x >= size)
+ return -1;
+ if (y < 0 || y >= size)
+ return -1;
+ if (z < 0 || z >= size)
+ return -1;
+
+ for (int i = 0; i < cell_subdiv - 1; i++) {
+
+ const Cell *bc = &cells[cell];
+
+ int child = 0;
+ if (x >= ofs_x + half) {
+ child |= 1;
+ ofs_x += half;
+ }
+ if (y >= ofs_y + half) {
+ child |= 2;
+ ofs_y += half;
+ }
+ if (z >= ofs_z + half) {
+ child |= 4;
+ ofs_z += half;
+ }
+
+ cell = bc->childs[child];
+ if (cell == CHILD_EMPTY)
+ return CHILD_EMPTY;
+
+ half >>= 1;
+ }
+
+ return cell;
+}
+void VoxelLightBaker::plot_light_directional(const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, bool p_direct) {
+
+ _check_init_light();
+
+ float max_len = Vector3(axis_cell_size[0], axis_cell_size[1], axis_cell_size[2]).length() * 1.1;
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Vector3 light_axis = p_direction;
+ Plane clip[3];
+ int clip_planes = 0;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+
+ for (int i = 0; i < 3; i++) {
+
+ if (ABS(light_axis[i]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[i] = 1.0;
+
+ if (light_axis[i] < 0) {
+
+ clip[clip_planes].d = axis_cell_size[i] + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ float distance_adv = _get_normal_advance(light_axis);
+
+ int success_count = 0;
+
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+ to += -light_axis.sign() * 0.47; //make it more likely to receive a ray
+
+ Vector3 from = to - max_len * light_axis;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+ distance += distance_adv - Math::fmod(distance, distance_adv); //make it reach the center of the box always
+ from = to - light_axis * distance;
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0];
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1];
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2];
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s;
+ light->direct_accum[i][1] += light_energy.y * s;
+ light->direct_accum[i][2] += light_energy.z * s;
+ }
+ success_count++;
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::plot_light_omni(const Vector3 &p_pos, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, bool p_direct) {
+
+ _check_init_light();
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Plane clip[3];
+ int clip_planes = 0;
+
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
+
+ Vector3 light_pos = to_cell_space.xform(p_pos) + Vector3(0.5, 0.5, 0.5);
+ //Vector3 spot_axis = -light_cache.transform.basis.get_axis(2).normalized();
+
+ float local_radius = to_cell_space.basis.xform(Vector3(0, 0, 1)).length() * p_radius;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+ to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
+
+ Vector3 light_axis = (to - light_pos).normalized();
+ float distance_adv = _get_normal_advance(light_axis);
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+
+ if (normal != Vector3() && normal.dot(-light_axis) < 0.001) {
+ idx = light_data[idx].next_leaf;
+ continue;
+ }
+
+ float att = 1.0;
+ {
+ float d = light_pos.distance_to(to);
+ if (d + distance_adv > local_radius) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float dt = CLAMP((d + distance_adv) / local_radius, 0, 1);
+ att *= powf(1.0 - dt, p_attenutation);
+ }
+#if 0
+ if (light_cache.type == VS::LIGHT_SPOT) {
+
+ float angle = Math::rad2deg(acos(light_axis.dot(spot_axis)));
+ if (angle > light_cache.spot_angle)
+ continue;
+
+ float d = CLAMP(angle / light_cache.spot_angle, 1, 0);
+ att *= powf(1.0 - d, light_cache.spot_attenuation);
+ }
+#endif
+ clip_planes = 0;
+
+ for (int c = 0; c < 3; c++) {
+
+ if (ABS(light_axis[c]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[c] = 1.0;
+
+ if (light_axis[c] < 0) {
+
+ clip[clip_planes].d = (1 << (cell_subdiv - 1)) + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ Vector3 from = light_pos;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+
+ distance -= Math::fmod(distance, distance_adv); //make it reach the center of the box always, but this tame make it closer
+ from = to - light_axis * distance;
+ to += (light_pos - to).sign() * 0.47; //make it more likely to receive a ray
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * att;
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s * att;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s * att;
+ light->direct_accum[i][1] += light_energy.y * s * att;
+ light->direct_accum[i][2] += light_energy.z * s * att;
+ }
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axis, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, float p_spot_angle, float p_spot_attenuation, bool p_direct) {
+
+ _check_init_light();
+
+ if (p_direct)
+ direct_lights_baked = true;
+
+ Plane clip[3];
+ int clip_planes = 0;
+
+ // uint64_t us = OS::get_singleton()->get_ticks_usec();
+
+ Vector3 light_pos = to_cell_space.xform(p_pos) + Vector3(0.5, 0.5, 0.5);
+ Vector3 spot_axis = to_cell_space.basis.xform(p_axis).normalized();
+
+ float local_radius = to_cell_space.basis.xform(Vector3(0, 0, 1)).length() * p_radius;
+
+ Light *light_data = bake_light.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ Vector3 light_energy = Vector3(p_color.r, p_color.g, p_color.b) * p_energy * p_indirect_energy;
+
+ int idx = first_leaf;
+ while (idx >= 0) {
+
+ //print_line("plot idx " + itos(idx));
+ Light *light = &light_data[idx];
+
+ Vector3 to(light->x + 0.5, light->y + 0.5, light->z + 0.5);
+
+ Vector3 light_axis = (to - light_pos).normalized();
+ float distance_adv = _get_normal_advance(light_axis);
+
+ Vector3 normal(cells[idx].normal[0], cells[idx].normal[1], cells[idx].normal[2]);
+
+ if (normal != Vector3() && normal.dot(-light_axis) < 0.001) {
+ idx = light_data[idx].next_leaf;
+ continue;
+ }
+
+ float angle = Math::rad2deg(Math::acos(light_axis.dot(-spot_axis)));
+ if (angle > p_spot_angle) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float att = Math::pow(1.0f - angle / p_spot_angle, p_spot_attenuation);
+
+ {
+ float d = light_pos.distance_to(to);
+ if (d + distance_adv > local_radius) {
+ idx = light_data[idx].next_leaf;
+ continue; // too far away
+ }
+
+ float dt = CLAMP((d + distance_adv) / local_radius, 0, 1);
+ att *= powf(1.0 - dt, p_attenutation);
+ }
+#if 0
+ if (light_cache.type == VS::LIGHT_SPOT) {
+
+ float angle = Math::rad2deg(acos(light_axis.dot(spot_axis)));
+ if (angle > light_cache.spot_angle)
+ continue;
+
+ float d = CLAMP(angle / light_cache.spot_angle, 1, 0);
+ att *= powf(1.0 - d, light_cache.spot_attenuation);
+ }
+#endif
+ clip_planes = 0;
+
+ for (int c = 0; c < 3; c++) {
+
+ if (ABS(light_axis[c]) < CMP_EPSILON)
+ continue;
+ clip[clip_planes].normal[c] = 1.0;
+
+ if (light_axis[c] < 0) {
+
+ clip[clip_planes].d = (1 << (cell_subdiv - 1)) + 1;
+ } else {
+ clip[clip_planes].d -= 1.0;
+ }
+
+ clip_planes++;
+ }
+
+ Vector3 from = light_pos;
+
+ for (int j = 0; j < clip_planes; j++) {
+
+ clip[j].intersects_segment(from, to, &from);
+ }
+
+ float distance = (to - from).length();
+
+ distance -= Math::fmod(distance, distance_adv); //make it reach the center of the box always, but this tame make it closer
+ from = to - light_axis * distance;
+
+ uint32_t result = 0xFFFFFFFF;
+
+ while (distance > -distance_adv) { //use this to avoid precision errors
+
+ result = _find_cell_at_pos(cells, int(floor(from.x)), int(floor(from.y)), int(floor(from.z)));
+ if (result != 0xFFFFFFFF) {
+ break;
+ }
+
+ from += light_axis * distance_adv;
+ distance -= distance_adv;
+ }
+
+ if (result == idx) {
+ //cell hit itself! hooray!
+
+ if (normal == Vector3()) {
+ for (int i = 0; i < 6; i++) {
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * att;
+ }
+
+ } else {
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-normal));
+ light->accum[i][0] += light_energy.x * cells[idx].albedo[0] * s * att;
+ light->accum[i][1] += light_energy.y * cells[idx].albedo[1] * s * att;
+ light->accum[i][2] += light_energy.z * cells[idx].albedo[2] * s * att;
+ }
+ }
+
+ for (int i = 0; i < 6; i++) {
+ float s = MAX(0.0, aniso_normal[i].dot(-light_axis)); //light depending on normal for direct
+ light->direct_accum[i][0] += light_energy.x * s * att;
+ light->direct_accum[i][1] += light_energy.y * s * att;
+ light->direct_accum[i][2] += light_energy.z * s * att;
+ }
+ }
+
+ idx = light_data[idx].next_leaf;
+ }
+}
+
+void VoxelLightBaker::_fixup_plot(int p_idx, int p_level) {
+
+ if (p_level == cell_subdiv - 1) {
+
+ leaf_voxel_count++;
+ float alpha = bake_cells[p_idx].alpha;
+
+ bake_cells[p_idx].albedo[0] /= alpha;
+ bake_cells[p_idx].albedo[1] /= alpha;
+ bake_cells[p_idx].albedo[2] /= alpha;
+
+ //transfer emission to light
+ bake_cells[p_idx].emission[0] /= alpha;
+ bake_cells[p_idx].emission[1] /= alpha;
+ bake_cells[p_idx].emission[2] /= alpha;
+
+ bake_cells[p_idx].normal[0] /= alpha;
+ bake_cells[p_idx].normal[1] /= alpha;
+ bake_cells[p_idx].normal[2] /= alpha;
+
+ Vector3 n(bake_cells[p_idx].normal[0], bake_cells[p_idx].normal[1], bake_cells[p_idx].normal[2]);
+ if (n.length() < 0.01) {
+ //too much fight over normal, zero it
+ bake_cells[p_idx].normal[0] = 0;
+ bake_cells[p_idx].normal[1] = 0;
+ bake_cells[p_idx].normal[2] = 0;
+ } else {
+ n.normalize();
+ bake_cells[p_idx].normal[0] = n.x;
+ bake_cells[p_idx].normal[1] = n.y;
+ bake_cells[p_idx].normal[2] = n.z;
+ }
+
+ bake_cells[p_idx].alpha = 1.0;
+
+ /*if (bake_light.size()) {
+ for(int i=0;i<6;i++) {
+
+ }
+ }*/
+
+ } else {
+
+ //go down
+
+ bake_cells[p_idx].emission[0] = 0;
+ bake_cells[p_idx].emission[1] = 0;
+ bake_cells[p_idx].emission[2] = 0;
+ bake_cells[p_idx].normal[0] = 0;
+ bake_cells[p_idx].normal[1] = 0;
+ bake_cells[p_idx].normal[2] = 0;
+ bake_cells[p_idx].albedo[0] = 0;
+ bake_cells[p_idx].albedo[1] = 0;
+ bake_cells[p_idx].albedo[2] = 0;
+ if (bake_light.size()) {
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] = 0;
+ bake_light[p_idx].accum[j][1] = 0;
+ bake_light[p_idx].accum[j][2] = 0;
+ }
+ }
+
+ float alpha_average = 0;
+ int children_found = 0;
+
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY)
+ continue;
+
+ _fixup_plot(child, p_level + 1);
+ alpha_average += bake_cells[child].alpha;
+
+ if (bake_light.size() > 0) {
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] += bake_light[child].accum[j][0];
+ bake_light[p_idx].accum[j][1] += bake_light[child].accum[j][1];
+ bake_light[p_idx].accum[j][2] += bake_light[child].accum[j][2];
+ }
+ bake_cells[p_idx].emission[0] += bake_cells[child].emission[0];
+ bake_cells[p_idx].emission[1] += bake_cells[child].emission[1];
+ bake_cells[p_idx].emission[2] += bake_cells[child].emission[2];
+ }
+
+ children_found++;
+ }
+
+ bake_cells[p_idx].alpha = alpha_average / 8.0;
+ if (bake_light.size() && children_found) {
+ float divisor = Math::lerp(8, children_found, propagation);
+ for (int j = 0; j < 6; j++) {
+ bake_light[p_idx].accum[j][0] /= divisor;
+ bake_light[p_idx].accum[j][1] /= divisor;
+ bake_light[p_idx].accum[j][2] /= divisor;
+ }
+ bake_cells[p_idx].emission[0] /= divisor;
+ bake_cells[p_idx].emission[1] /= divisor;
+ bake_cells[p_idx].emission[2] /= divisor;
+ }
+ }
+}
+
+//make sure any cell (save for the root) has an empty cell previous to it, so it can be interpolated into
+
+void VoxelLightBaker::_plot_triangle(Vector2 *vertices, Vector3 *positions, Vector3 *normals, LightMap *pixels, int width, int height) {
+
+ int x[3];
+ int y[3];
+
+ for (int j = 0; j < 3; j++) {
+
+ x[j] = vertices[j].x * width;
+ y[j] = vertices[j].y * height;
+ //x[j] = CLAMP(x[j], 0, bt.width - 1);
+ //y[j] = CLAMP(y[j], 0, bt.height - 1);
+ }
+
+ // sort the points vertically
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(positions[1], positions[2]);
+ SWAP(normals[1], normals[2]);
+ }
+ if (y[0] > y[1]) {
+ SWAP(x[0], x[1]);
+ SWAP(y[0], y[1]);
+ SWAP(positions[0], positions[1]);
+ SWAP(normals[0], normals[1]);
+ }
+ if (y[1] > y[2]) {
+ SWAP(x[1], x[2]);
+ SWAP(y[1], y[2]);
+ SWAP(positions[1], positions[2]);
+ SWAP(normals[1], normals[2]);
+ }
+
+ double dx_far = double(x[2] - x[0]) / (y[2] - y[0] + 1);
+ double dx_upper = double(x[1] - x[0]) / (y[1] - y[0] + 1);
+ double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
+ double xf = x[0];
+ double xt = x[0] + dx_upper; // if y[0] == y[1], special case
+ for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) {
+ if (yi >= 0) {
+ for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) {
+ //pixels[int(x + y * width)] = color;
+
+ Vector2 v0 = Vector2(x[1] - x[0], y[1] - y[0]);
+ Vector2 v1 = Vector2(x[2] - x[0], y[2] - y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi - x[0], yi - y[0]);
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ Vector3 normal;
+ if (denom == 0) {
+ pos = positions[0];
+ normal = normals[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = positions[0] * u + positions[1] * v + positions[2] * w;
+ normal = normals[0] * u + normals[1] * v + normals[2] * w;
+ }
+
+ int ofs = yi * width + xi;
+ pixels[ofs].normal = normal;
+ pixels[ofs].pos = pos;
+ }
+
+ for (int xi = (xf < width ? int(xf) : width - 1); xi >= (xt > 0 ? xt : 0); xi--) {
+ //pixels[int(x + y * width)] = color;
+ Vector2 v0 = Vector2(x[1] - x[0], y[1] - y[0]);
+ Vector2 v1 = Vector2(x[2] - x[0], y[2] - y[0]);
+ //vertices[2] - vertices[0];
+ Vector2 v2 = Vector2(xi - x[0], yi - y[0]);
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ Vector3 pos;
+ Vector3 normal;
+ if (denom == 0) {
+ pos = positions[0];
+ normal = normals[0];
+ } else {
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+ pos = positions[0] * u + positions[1] * v + positions[2] * w;
+ normal = normals[0] * u + normals[1] * v + normals[2] * w;
+ }
+
+ int ofs = yi * width + xi;
+ pixels[ofs].normal = normal;
+ pixels[ofs].pos = pos;
+ }
+ }
+ xf += dx_far;
+ if (yi < y[1])
+ xt += dx_upper;
+ else
+ xt += dx_low;
+ }
+}
+
+void VoxelLightBaker::_sample_baked_octree_filtered_and_anisotropic(const Vector3 &p_posf, const Vector3 &p_direction, float p_level, Vector3 &r_color, float &r_alpha) {
+
+ int size = 1 << (cell_subdiv - 1);
+
+ int clamp_v = size - 1;
+ //first of all, clamp
+ Vector3 pos;
+ pos.x = CLAMP(p_posf.x, 0, clamp_v);
+ pos.y = CLAMP(p_posf.y, 0, clamp_v);
+ pos.z = CLAMP(p_posf.z, 0, clamp_v);
+
+ float level = (cell_subdiv - 1) - p_level;
+
+ int target_level;
+ float level_filter;
+ if (level <= 0.0) {
+ level_filter = 0;
+ target_level = 0;
+ } else {
+ target_level = Math::ceil(level);
+ level_filter = target_level - level;
+ }
+
+ const Cell *cells = bake_cells.ptr();
+ const Light *light = bake_light.ptr();
+
+ Vector3 color[2][8];
+ float alpha[2][8];
+ zeromem(alpha, sizeof(float) * 2 * 8);
+
+ //find cell at given level first
+
+ for (int c = 0; c < 2; c++) {
+
+ int current_level = MAX(0, target_level - c);
+ int level_cell_size = (1 << (cell_subdiv - 1)) >> current_level;
+
+ for (int n = 0; n < 8; n++) {
+
+ int x = int(pos.x);
+ int y = int(pos.y);
+ int z = int(pos.z);
+
+ if (n & 1)
+ x += level_cell_size;
+ if (n & 2)
+ y += level_cell_size;
+ if (n & 4)
+ z += level_cell_size;
+
+ int ofs_x = 0;
+ int ofs_y = 0;
+ int ofs_z = 0;
+
+ x = CLAMP(x, 0, clamp_v);
+ y = CLAMP(y, 0, clamp_v);
+ z = CLAMP(z, 0, clamp_v);
+
+ int half = size / 2;
+ uint32_t cell = 0;
+ for (int i = 0; i < current_level; i++) {
+
+ const Cell *bc = &cells[cell];
+
+ int child = 0;
+ if (x >= ofs_x + half) {
+ child |= 1;
+ ofs_x += half;
+ }
+ if (y >= ofs_y + half) {
+ child |= 2;
+ ofs_y += half;
+ }
+ if (z >= ofs_z + half) {
+ child |= 4;
+ ofs_z += half;
+ }
+
+ cell = bc->childs[child];
+ if (cell == CHILD_EMPTY)
+ break;
+
+ half >>= 1;
+ }
+
+ if (cell == CHILD_EMPTY) {
+ alpha[c][n] = 0;
+ } else {
+ alpha[c][n] = cells[cell].alpha;
+
+ for (int i = 0; i < 6; i++) {
+ //anisotropic read light
+ float amount = p_direction.dot(aniso_normal[i]);
+ //if (c == 0) {
+ // print_line("\t" + itos(n) + " aniso " + itos(i) + " " + rtos(light[cell].accum[i][0]) + " VEC: " + aniso_normal[i]);
+ //}
+ if (amount < 0)
+ amount = 0;
+ //amount = 1;
+ color[c][n].x += light[cell].accum[i][0] * amount;
+ color[c][n].y += light[cell].accum[i][1] * amount;
+ color[c][n].z += light[cell].accum[i][2] * amount;
+ }
+
+ color[c][n].x += cells[cell].emission[0];
+ color[c][n].y += cells[cell].emission[1];
+ color[c][n].z += cells[cell].emission[2];
+ }
+
+ //print_line("\tlev " + itos(c) + " - " + itos(n) + " alpha: " + rtos(cells[test_cell].alpha) + " col: " + color[c][n]);
+ }
+ }
+
+ float target_level_size = size >> target_level;
+ Vector3 pos_fract[2];
+
+ pos_fract[0].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[0].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[0].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ target_level_size = size >> MAX(0, target_level - 1);
+
+ pos_fract[1].x = Math::fmod(pos.x, target_level_size) / target_level_size;
+ pos_fract[1].y = Math::fmod(pos.y, target_level_size) / target_level_size;
+ pos_fract[1].z = Math::fmod(pos.z, target_level_size) / target_level_size;
+
+ float alpha_interp[2];
+ Vector3 color_interp[2];
+
+ for (int i = 0; i < 2; i++) {
+
+ Vector3 color_x00 = color[i][0].linear_interpolate(color[i][1], pos_fract[i].x);
+ Vector3 color_xy0 = color[i][2].linear_interpolate(color[i][3], pos_fract[i].x);
+ Vector3 blend_z0 = color_x00.linear_interpolate(color_xy0, pos_fract[i].y);
+
+ Vector3 color_x0z = color[i][4].linear_interpolate(color[i][5], pos_fract[i].x);
+ Vector3 color_xyz = color[i][6].linear_interpolate(color[i][7], pos_fract[i].x);
+ Vector3 blend_z1 = color_x0z.linear_interpolate(color_xyz, pos_fract[i].y);
+
+ color_interp[i] = blend_z0.linear_interpolate(blend_z1, pos_fract[i].z);
+
+ float alpha_x00 = Math::lerp(alpha[i][0], alpha[i][1], pos_fract[i].x);
+ float alpha_xy0 = Math::lerp(alpha[i][2], alpha[i][3], pos_fract[i].x);
+ float alpha_z0 = Math::lerp(alpha_x00, alpha_xy0, pos_fract[i].y);
+
+ float alpha_x0z = Math::lerp(alpha[i][4], alpha[i][5], pos_fract[i].x);
+ float alpha_xyz = Math::lerp(alpha[i][6], alpha[i][7], pos_fract[i].x);
+ float alpha_z1 = Math::lerp(alpha_x0z, alpha_xyz, pos_fract[i].y);
+
+ alpha_interp[i] = Math::lerp(alpha_z0, alpha_z1, pos_fract[i].z);
+ }
+
+ r_color = color_interp[0].linear_interpolate(color_interp[1], level_filter);
+ r_alpha = Math::lerp(alpha_interp[0], alpha_interp[1], level_filter);
+
+ // print_line("pos: " + p_posf + " level " + rtos(p_level) + " down to " + itos(target_level) + "." + rtos(level_filter) + " color " + r_color + " alpha " + rtos(r_alpha));
+}
+
+Vector3 VoxelLightBaker::_voxel_cone_trace(const Vector3 &p_pos, const Vector3 &p_normal, float p_aperture) {
+
+ float bias = 2.5;
+ float max_distance = (Vector3(1, 1, 1) * (1 << (cell_subdiv - 1))).length();
+
+ float dist = bias;
+ float alpha = 0.0;
+ Vector3 color;
+
+ Vector3 scolor;
+ float salpha;
+
+ while (dist < max_distance && alpha < 0.95) {
+ float diameter = MAX(1.0, 2.0 * p_aperture * dist);
+ //print_line("VCT: pos " + (p_pos + dist * p_normal) + " dist " + rtos(dist) + " mipmap " + rtos(log2(diameter)) + " alpha " + rtos(alpha));
+ //Plane scolor = textureLod(probe, (pos + dist * direction) * cell_size, log2(diameter) );
+ _sample_baked_octree_filtered_and_anisotropic(p_pos + dist * p_normal, p_normal, log2(diameter), scolor, salpha);
+ float a = (1.0 - alpha);
+ color += scolor * a;
+ alpha += a * salpha;
+ dist += diameter * 0.5;
+ }
+
+ /*if (blend_ambient) {
+ color.rgb = mix(ambient,color.rgb,min(1.0,alpha/0.95));
+ }*/
+
+ return color;
+}
+
+Vector3 VoxelLightBaker::_compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal) {
+
+ //find arbitrary tangent and bitangent, then build a matrix
+ Vector3 v0 = Math::abs(p_normal.z) < 0.999 ? Vector3(0, 0, 1) : Vector3(0, 1, 0);
+ Vector3 tangent = v0.cross(p_normal).normalized();
+ Vector3 bitangent = tangent.cross(p_normal).normalized();
+ Basis normal_xform = Basis(tangent, bitangent, p_normal).transposed();
+
+ // print_line("normal xform: " + normal_xform);
+ const Vector3 *cone_dirs;
+ const float *cone_weights;
+ int cone_dir_count;
+ float cone_aperture;
+
+ switch (bake_quality) {
+ case BAKE_QUALITY_LOW: {
+ //default quality
+ static const Vector3 dirs[4] = {
+ Vector3(0.707107, 0, 0.707107),
+ Vector3(0, 0.707107, 0.707107),
+ Vector3(-0.707107, 0, 0.707107),
+ Vector3(0, -0.707107, 0.707107)
+ };
+
+ static const float weights[4] = { 0.25, 0.25, 0.25, 0.25 };
+
+ cone_dirs = dirs;
+ cone_dir_count = 4;
+ cone_aperture = 1.0; // tan(angle) 90 degrees
+ cone_weights = weights;
+ } break;
+ case BAKE_QUALITY_MEDIUM: {
+ //default quality
+ static const Vector3 dirs[6] = {
+ Vector3(0, 0, 1),
+ Vector3(0.866025, 0, 0.5),
+ Vector3(0.267617, 0.823639, 0.5),
+ Vector3(-0.700629, 0.509037, 0.5),
+ Vector3(-0.700629, -0.509037, 0.5),
+ Vector3(0.267617, -0.823639, 0.5)
+ };
+ static const float weights[6] = { 0.25, 0.15, 0.15, 0.15, 0.15, 0.15 };
+ //
+ cone_dirs = dirs;
+ cone_dir_count = 6;
+ cone_aperture = 0.577; // tan(angle) 60 degrees
+ cone_weights = weights;
+ } break;
+ case BAKE_QUALITY_HIGH: {
+
+ //high qualily
+ static const Vector3 dirs[10] = {
+ Vector3(0.8781648411741658, 0.0, 0.478358141694643),
+ Vector3(0.5369754325592234, 0.6794204427701518, 0.5000452447267606),
+ Vector3(-0.19849436573466497, 0.8429904390140635, 0.49996710542041645),
+ Vector3(-0.7856196499811189, 0.3639120321329737, 0.5003696617825604),
+ Vector3(-0.7856196499811189, -0.3639120321329737, 0.5003696617825604),
+ Vector3(-0.19849436573466497, -0.8429904390140635, 0.49996710542041645),
+ Vector3(0.5369754325592234, -0.6794204427701518, 0.5000452447267606),
+ Vector3(-0.4451656858129485, 0.0, 0.8954482185892644),
+ Vector3(0.19124006749743122, 0.39355745585016605, 0.8991883926788214),
+ Vector3(0.19124006749743122, -0.39355745585016605, 0.8991883926788214),
+ };
+ static const float weights[10] = { 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.08571, 0.133333, 0.133333, 0.13333 };
+ cone_dirs = dirs;
+ cone_dir_count = 10;
+ cone_aperture = 0.404; // tan(angle) 45 degrees
+ cone_weights = weights;
+ } break;
+ }
+
+ Vector3 accum;
+
+ for (int i = 0; i < cone_dir_count; i++) {
+ // if (i > 0)
+ // continue;
+ Vector3 dir = normal_xform.xform(cone_dirs[i]).normalized(); //normal may not completely correct when transformed to cell
+ //print_line("direction: " + dir);
+ accum += _voxel_cone_trace(p_pos, dir, cone_aperture) * cone_weights[i];
+ }
+
+ return accum;
+}
+
+Vector3 VoxelLightBaker::_compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal) {
+
+ int samples_per_quality[3] = { 48, 128, 512 };
+
+ int samples = samples_per_quality[bake_quality];
+
+ //create a basis in Z
+ Vector3 v0 = Math::abs(p_normal.z) < 0.999 ? Vector3(0, 0, 1) : Vector3(0, 1, 0);
+ Vector3 tangent = v0.cross(p_normal).normalized();
+ Vector3 bitangent = tangent.cross(p_normal).normalized();
+ Basis normal_xform = Basis(tangent, bitangent, p_normal).transposed();
+
+ float bias = 1.5;
+ int max_level = cell_subdiv - 1;
+ int size = 1 << max_level;
+
+ Vector3 accum;
+ float spread = Math::deg2rad(80.0);
+
+ const Light *light = bake_light.ptr();
+ const Cell *cells = bake_cells.ptr();
+
+ for (int i = 0; i < samples; i++) {
+
+ float random_angle1 = (((Math::rand() % 65535) / 65535.0) * 2.0 - 1.0) * spread;
+ Vector3 axis(0, sin(random_angle1), cos(random_angle1));
+ float random_angle2 = ((Math::rand() % 65535) / 65535.0) * Math_PI * 2.0;
+ Basis rot(Vector3(0, 0, 1), random_angle2);
+ axis = rot.xform(axis);
+
+ Vector3 direction = normal_xform.xform(axis).normalized();
+
+ Vector3 pos = p_pos + Vector3(0.5, 0.5, 0.5) + direction * bias;
+
+ Vector3 advance = direction * _get_normal_advance(direction);
+
+ uint32_t cell = CHILD_EMPTY;
+
+ while (cell == CHILD_EMPTY) {
+
+ int x = int(pos.x);
+ int y = int(pos.y);
+ int z = int(pos.z);
+
+ int ofs_x = 0;
+ int ofs_y = 0;
+ int ofs_z = 0;
+ int half = size / 2;
+
+ if (x < 0 || x >= size)
+ break;
+ if (y < 0 || y >= size)
+ break;
+ if (z < 0 || z >= size)
+ break;
+
+ //int level_limit = max_level;
+
+ cell = 0; //start from root
+ for (int i = 0; i < max_level; i++) {
+
+ const Cell *bc = &cells[cell];
+
+ int child = 0;
+ if (x >= ofs_x + half) {
+ child |= 1;
+ ofs_x += half;
+ }
+ if (y >= ofs_y + half) {
+ child |= 2;
+ ofs_y += half;
+ }
+ if (z >= ofs_z + half) {
+ child |= 4;
+ ofs_z += half;
+ }
+
+ cell = bc->childs[child];
+ if (cell == CHILD_EMPTY)
+ break;
+
+ half >>= 1;
+ }
+
+ pos += advance;
+ }
+
+ if (cell != CHILD_EMPTY) {
+ for (int i = 0; i < 6; i++) {
+ //anisotropic read light
+ float amount = direction.dot(aniso_normal[i]);
+ if (amount < 0)
+ amount = 0;
+ accum.x += light[cell].accum[i][0] * amount;
+ accum.y += light[cell].accum[i][1] * amount;
+ accum.z += light[cell].accum[i][2] * amount;
+ }
+ }
+ }
+
+ return accum / samples;
+}
+
+Error VoxelLightBaker::make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float), void *p_bake_time_ud) {
+
+ //transfer light information to a lightmap
+ Ref<Mesh> mesh = p_mesh;
+
+ int width = mesh->get_lightmap_size_hint().x;
+ int height = mesh->get_lightmap_size_hint().y;
+
+ //step 1 - create lightmap
+ Vector<LightMap> lightmap;
+ lightmap.resize(width * height);
+
+ Transform xform = to_cell_space * p_xform;
+
+ //step 2 plot faces to lightmap
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ Array arrays = mesh->surface_get_arrays(i);
+ PoolVector<Vector3> vertices = arrays[Mesh::ARRAY_VERTEX];
+ PoolVector<Vector3> normals = arrays[Mesh::ARRAY_NORMAL];
+ PoolVector<Vector2> uv2 = arrays[Mesh::ARRAY_TEX_UV2];
+ PoolVector<int> indices = arrays[Mesh::ARRAY_INDEX];
+
+ ERR_FAIL_COND_V(vertices.size() == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(normals.size() == 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(uv2.size() == 0, ERR_INVALID_PARAMETER);
+
+ int vc = vertices.size();
+ PoolVector<Vector3>::Read vr = vertices.read();
+ PoolVector<Vector3>::Read nr = normals.read();
+ PoolVector<Vector2>::Read u2r = uv2.read();
+ PoolVector<int>::Read ir;
+ int ic = 0;
+
+ if (indices.size()) {
+ ic = indices.size();
+ ir = indices.read();
+ }
+
+ int faces = ic ? ic / 3 : vc / 3;
+ for (int i = 0; i < faces; i++) {
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+ for (int j = 0; j < 3; j++) {
+ int idx = ic ? ir[i * 3 + j] : i * 3 + j;
+ vertex[j] = xform.xform(vr[idx]);
+ normal[j] = xform.basis.xform(nr[idx]).normalized();
+ uv[j] = u2r[idx];
+ }
+
+ _plot_triangle(uv, vertex, normal, lightmap.ptrw(), width, height);
+ }
+ }
+ //step 3 perform voxel cone trace on lightmap pixels
+
+ {
+ LightMap *lightmap_ptr = lightmap.ptrw();
+ uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
+ volatile int lines = 0;
+
+ for (int i = 0; i < height; i++) {
+
+ //print_line("bake line " + itos(i) + " / " + itos(height));
+#ifdef _OPENMP
+#pragma omp parallel for
+#endif
+ for (int j = 0; j < width; j++) {
+
+ //if (i == 125 && j == 280) {
+
+ LightMap *pixel = &lightmap_ptr[i * width + j];
+ if (pixel->pos == Vector3())
+ continue; //unused, skipe
+
+ //print_line("pos: " + pixel->pos + " normal " + pixel->normal);
+ switch (bake_mode) {
+ case BAKE_MODE_CONE_TRACE: {
+ pixel->light = _compute_pixel_light_at_pos(pixel->pos, pixel->normal) * energy;
+ } break;
+ case BAKE_MODE_RAY_TRACE: {
+ pixel->light = _compute_ray_trace_at_pos(pixel->pos, pixel->normal) * energy;
+ } break;
+ // pixel->light = Vector3(1, 1, 1);
+ //}
+ }
+ }
+
+ lines = MAX(lines, i); //for multithread
+ if (p_bake_time_func) {
+ uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time;
+ float elapsed_sec = double(elapsed) / 1000000.0;
+ float remaining = lines < 1 ? 0 : (elapsed_sec / lines) * (height - lines - 1);
+ if (p_bake_time_func(p_bake_time_ud, remaining, lines / float(height))) {
+ return ERR_SKIP;
+ }
+ }
+ }
+
+ if (bake_mode == BAKE_MODE_RAY_TRACE) {
+ //blur
+ print_line("bluring, use pos for separatable copy");
+ //gauss kernel, 7 step sigma 2
+ static const float gauss_kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
+ //horizontal pass
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal == Vector3()) {
+ continue; //empty
+ }
+ float gauss_sum = gauss_kernel[0];
+ Vector3 accum = lightmap_ptr[i * width + j].light * gauss_kernel[0];
+ for (int k = 1; k < 4; k++) {
+ int new_x = j + k;
+ if (new_x >= width || lightmap_ptr[i * width + new_x].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[i * width + new_x].light * gauss_kernel[k];
+ }
+ for (int k = 1; k < 4; k++) {
+ int new_x = j - k;
+ if (new_x < 0 || lightmap_ptr[i * width + new_x].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[i * width + new_x].light * gauss_kernel[k];
+ }
+
+ lightmap_ptr[i * width + j].pos = accum /= gauss_sum;
+ }
+ }
+ //vertical pass
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal == Vector3())
+ continue; //empty, dont write over it anyway
+ float gauss_sum = gauss_kernel[0];
+ Vector3 accum = lightmap_ptr[i * width + j].pos * gauss_kernel[0];
+ for (int k = 1; k < 4; k++) {
+ int new_y = i + k;
+ if (new_y >= height || lightmap_ptr[new_y * width + j].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[new_y * width + j].pos * gauss_kernel[k];
+ }
+ for (int k = 1; k < 4; k++) {
+ int new_y = i - k;
+ if (new_y < 0 || lightmap_ptr[new_y * width + j].normal == Vector3())
+ break;
+ gauss_sum += gauss_kernel[k];
+ accum += lightmap_ptr[new_y * width + j].pos * gauss_kernel[k];
+ }
+
+ lightmap_ptr[i * width + j].light = accum /= gauss_sum;
+ }
+ }
+ }
+
+ //add directional light (do this after blur)
+ {
+ LightMap *lightmap_ptr = lightmap.ptrw();
+ const Cell *cells = bake_cells.ptr();
+ const Light *light = bake_light.ptr();
+
+ for (int i = 0; i < height; i++) {
+
+ //print_line("bake line " + itos(i) + " / " + itos(height));
+#ifdef _OPENMP
+#pragma omp parallel for
+#endif
+ for (int j = 0; j < width; j++) {
+
+ //if (i == 125 && j == 280) {
+
+ LightMap *pixel = &lightmap_ptr[i * width + j];
+ if (pixel->pos == Vector3())
+ continue; //unused, skipe
+
+ int x = int(pixel->pos.x) - 1;
+ int y = int(pixel->pos.y) - 1;
+ int z = int(pixel->pos.z) - 1;
+ Color accum;
+ int size = 1 << (cell_subdiv - 1);
+
+ int found = 0;
+
+ for (int k = 0; k < 8; k++) {
+
+ int ofs_x = x;
+ int ofs_y = y;
+ int ofs_z = z;
+
+ if (k & 1)
+ ofs_x++;
+ if (k & 2)
+ ofs_y++;
+ if (k & 4)
+ ofs_z++;
+
+ if (x < 0 || x >= size)
+ continue;
+ if (y < 0 || y >= size)
+ continue;
+ if (z < 0 || z >= size)
+ continue;
+
+ uint32_t cell = _find_cell_at_pos(cells, ofs_x, ofs_y, ofs_z);
+
+ if (cell == CHILD_EMPTY)
+ continue;
+ for (int l = 0; l < 6; l++) {
+ float s = pixel->normal.dot(aniso_normal[l]);
+ if (s < 0)
+ s = 0;
+ accum.r += light[cell].direct_accum[l][0] * s;
+ accum.g += light[cell].direct_accum[l][1] * s;
+ accum.b += light[cell].direct_accum[l][2] * s;
+ }
+ found++;
+ }
+ if (found) {
+ accum /= found;
+ pixel->light.x += accum.r;
+ pixel->light.y += accum.g;
+ pixel->light.z += accum.b;
+ }
+ }
+ }
+ }
+
+ {
+ //fill gaps with neighbour vertices to avoid filter fades to black on edges
+
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ if (lightmap_ptr[i * width + j].normal != Vector3()) {
+ continue; //filled, skip
+ }
+
+ //this can't be made separatable..
+
+ int closest_i = -1, closest_j = 1;
+ float closest_dist = 1e20;
+
+ const int margin = 3;
+ for (int y = i - margin; y <= i + margin; y++) {
+ for (int x = j - margin; x <= j + margin; x++) {
+
+ if (x == j && y == i)
+ continue;
+ if (x < 0 || x >= width)
+ continue;
+ if (y < 0 || y >= height)
+ continue;
+ if (lightmap_ptr[y * width + x].normal == Vector3())
+ continue; //also ensures that blitted stuff is not reused
+
+ float dist = Vector2(i - y, j - x).length();
+ if (dist > closest_dist)
+ continue;
+
+ closest_dist = dist;
+ closest_i = y;
+ closest_j = x;
+ }
+ }
+
+ if (closest_i != -1) {
+ lightmap_ptr[i * width + j].light = lightmap_ptr[closest_i * width + closest_j].light;
+ }
+ }
+ }
+ }
+
+ {
+ //fill the lightmap data
+ r_lightmap.width = width;
+ r_lightmap.height = height;
+ r_lightmap.light.resize(lightmap.size() * 3);
+ PoolVector<float>::Write w = r_lightmap.light.write();
+ for (int i = 0; i < lightmap.size(); i++) {
+ w[i * 3 + 0] = lightmap[i].light.x;
+ w[i * 3 + 1] = lightmap[i].light.y;
+ w[i * 3 + 2] = lightmap[i].light.z;
+ }
+ }
+
+#if 0
+ {
+ PoolVector<uint8_t> img;
+ int ls = lightmap.size();
+ img.resize(ls * 3);
+ {
+ PoolVector<uint8_t>::Write w = img.write();
+ for (int i = 0; i < ls; i++) {
+ w[i * 3 + 0] = CLAMP(lightmap_ptr[i].light.x * 255, 0, 255);
+ w[i * 3 + 1] = CLAMP(lightmap_ptr[i].light.y * 255, 0, 255);
+ w[i * 3 + 2] = CLAMP(lightmap_ptr[i].light.z * 255, 0, 255);
+ //w[i * 3 + 0] = CLAMP(lightmap_ptr[i].normal.x * 255, 0, 255);
+ //w[i * 3 + 1] = CLAMP(lightmap_ptr[i].normal.y * 255, 0, 255);
+ //w[i * 3 + 2] = CLAMP(lightmap_ptr[i].normal.z * 255, 0, 255);
+ //w[i * 3 + 0] = CLAMP(lightmap_ptr[i].pos.x / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ //w[i * 3 + 1] = CLAMP(lightmap_ptr[i].pos.y / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ //w[i * 3 + 2] = CLAMP(lightmap_ptr[i].pos.z / (1 << (cell_subdiv - 1)) * 255, 0, 255);
+ }
+ }
+
+ Ref<Image> image;
+ image.instance();
+ image->create(width, height, false, Image::FORMAT_RGB8, img);
+
+ String name = p_mesh->get_name();
+ if (name == "") {
+ name = "Mesh" + itos(p_mesh->get_instance_id());
+ }
+ image->save_png(name + ".png");
+ }
+#endif
+ }
+
+ return OK;
+}
+
+void VoxelLightBaker::begin_bake(int p_subdiv, const AABB &p_bounds) {
+
+ original_bounds = p_bounds;
+ cell_subdiv = p_subdiv;
+ bake_cells.resize(1);
+ material_cache.clear();
+
+ //find out the actual real bounds, power of 2, which gets the highest subdivision
+ po2_bounds = p_bounds;
+ int longest_axis = po2_bounds.get_longest_axis_index();
+ axis_cell_size[longest_axis] = (1 << (cell_subdiv - 1));
+ leaf_voxel_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ if (i == longest_axis)
+ continue;
+
+ axis_cell_size[i] = axis_cell_size[longest_axis];
+ float axis_size = po2_bounds.size[longest_axis];
+
+ //shrink until fit subdiv
+ while (axis_size / 2.0 >= po2_bounds.size[i]) {
+ axis_size /= 2.0;
+ axis_cell_size[i] >>= 1;
+ }
+
+ po2_bounds.size[i] = po2_bounds.size[longest_axis];
+ }
+
+ Transform to_bounds;
+ to_bounds.basis.scale(Vector3(po2_bounds.size[longest_axis], po2_bounds.size[longest_axis], po2_bounds.size[longest_axis]));
+ to_bounds.origin = po2_bounds.position;
+
+ Transform to_grid;
+ to_grid.basis.scale(Vector3(axis_cell_size[longest_axis], axis_cell_size[longest_axis], axis_cell_size[longest_axis]));
+
+ to_cell_space = to_grid * to_bounds.affine_inverse();
+
+ cell_size = po2_bounds.size[longest_axis] / axis_cell_size[longest_axis];
+}
+
+void VoxelLightBaker::end_bake() {
+ _fixup_plot(0, 0);
+}
+
+//create the data for visual server
+
+PoolVector<int> VoxelLightBaker::create_gi_probe_data() {
+
+ PoolVector<int> data;
+
+ data.resize(16 + (8 + 1 + 1 + 1 + 1) * bake_cells.size()); //4 for header, rest for rest.
+
+ {
+ PoolVector<int>::Write w = data.write();
+
+ uint32_t *w32 = (uint32_t *)w.ptr();
+
+ w32[0] = 0; //version
+ w32[1] = cell_subdiv; //subdiv
+ w32[2] = axis_cell_size[0];
+ w32[3] = axis_cell_size[1];
+ w32[4] = axis_cell_size[2];
+ w32[5] = bake_cells.size();
+ w32[6] = leaf_voxel_count;
+
+ int ofs = 16;
+
+ for (int i = 0; i < bake_cells.size(); i++) {
+
+ for (int j = 0; j < 8; j++) {
+ w32[ofs++] = bake_cells[i].childs[j];
+ }
+
+ { //albedo
+ uint32_t rgba = uint32_t(CLAMP(bake_cells[i].albedo[0] * 255.0, 0, 255)) << 16;
+ rgba |= uint32_t(CLAMP(bake_cells[i].albedo[1] * 255.0, 0, 255)) << 8;
+ rgba |= uint32_t(CLAMP(bake_cells[i].albedo[2] * 255.0, 0, 255)) << 0;
+
+ w32[ofs++] = rgba;
+ }
+ { //emission
+
+ Vector3 e(bake_cells[i].emission[0], bake_cells[i].emission[1], bake_cells[i].emission[2]);
+ float l = e.length();
+ if (l > 0) {
+ e.normalize();
+ l = CLAMP(l / 8.0, 0, 1.0);
+ }
+
+ uint32_t em = uint32_t(CLAMP(e[0] * 255, 0, 255)) << 24;
+ em |= uint32_t(CLAMP(e[1] * 255, 0, 255)) << 16;
+ em |= uint32_t(CLAMP(e[2] * 255, 0, 255)) << 8;
+ em |= uint32_t(CLAMP(l * 255, 0, 255));
+
+ w32[ofs++] = em;
+ }
+
+ //w32[ofs++]=bake_cells[i].used_sides;
+ { //normal
+
+ Vector3 n(bake_cells[i].normal[0], bake_cells[i].normal[1], bake_cells[i].normal[2]);
+ n = n * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5);
+ uint32_t norm = 0;
+
+ norm |= uint32_t(CLAMP(n.x * 255.0, 0, 255)) << 16;
+ norm |= uint32_t(CLAMP(n.y * 255.0, 0, 255)) << 8;
+ norm |= uint32_t(CLAMP(n.z * 255.0, 0, 255)) << 0;
+
+ w32[ofs++] = norm;
+ }
+
+ {
+ uint16_t alpha = CLAMP(uint32_t(bake_cells[i].alpha * 65535.0), 0, 65535);
+ uint16_t level = bake_cells[i].level;
+
+ w32[ofs++] = (uint32_t(level) << 16) | uint32_t(alpha);
+ }
+ }
+ }
+
+ return data;
+}
+
+void VoxelLightBaker::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode) {
+
+ if (p_level == cell_subdiv - 1) {
+
+ Vector3 center = p_aabb.position + p_aabb.size * 0.5;
+ Transform xform;
+ xform.origin = center;
+ xform.basis.scale(p_aabb.size * 0.5);
+ p_multimesh->set_instance_transform(idx, xform);
+ Color col;
+ if (p_mode == DEBUG_ALBEDO) {
+ col = Color(bake_cells[p_idx].albedo[0], bake_cells[p_idx].albedo[1], bake_cells[p_idx].albedo[2]);
+ } else if (p_mode == DEBUG_LIGHT) {
+ for (int i = 0; i < 6; i++) {
+ col.r += bake_light[p_idx].accum[i][0];
+ col.g += bake_light[p_idx].accum[i][1];
+ col.b += bake_light[p_idx].accum[i][2];
+ col.r += bake_light[p_idx].direct_accum[i][0];
+ col.g += bake_light[p_idx].direct_accum[i][1];
+ col.b += bake_light[p_idx].direct_accum[i][2];
+ }
+ }
+ //Color col = Color(bake_cells[p_idx].emission[0], bake_cells[p_idx].emission[1], bake_cells[p_idx].emission[2]);
+ p_multimesh->set_instance_color(idx, col);
+
+ idx++;
+
+ } else {
+
+ for (int i = 0; i < 8; i++) {
+
+ uint32_t child = bake_cells[p_idx].childs[i];
+
+ if (child == CHILD_EMPTY || child >= max_original_cells)
+ continue;
+
+ AABB aabb = p_aabb;
+ aabb.size *= 0.5;
+
+ if (i & 1)
+ aabb.position.x += aabb.size.x;
+ if (i & 2)
+ aabb.position.y += aabb.size.y;
+ if (i & 4)
+ aabb.position.z += aabb.size.z;
+
+ _debug_mesh(bake_cells[p_idx].childs[i], p_level + 1, aabb, p_multimesh, idx, p_mode);
+ }
+ }
+}
+
+Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
+
+ Ref<MultiMesh> mm;
+
+ ERR_FAIL_COND_V(p_mode == DEBUG_LIGHT && bake_light.size() == 0, mm);
+ mm.instance();
+
+ mm->set_transform_format(MultiMesh::TRANSFORM_3D);
+ mm->set_color_format(MultiMesh::COLOR_8BIT);
+ print_line("leaf voxels: " + itos(leaf_voxel_count));
+ mm->set_instance_count(leaf_voxel_count);
+
+ Ref<ArrayMesh> mesh;
+ mesh.instance();
+
+ {
+ Array arr;
+ arr.resize(Mesh::ARRAY_MAX);
+
+ PoolVector<Vector3> vertices;
+ PoolVector<Color> colors;
+
+ int vtx_idx = 0;
+#define ADD_VTX(m_idx) \
+ ; \
+ vertices.push_back(face_points[m_idx]); \
+ colors.push_back(Color(1, 1, 1, 1)); \
+ vtx_idx++;
+
+ for (int i = 0; i < 6; i++) {
+
+ Vector3 face_points[4];
+
+ for (int j = 0; j < 4; j++) {
+
+ float v[3];
+ v[0] = 1.0;
+ v[1] = 1 - 2 * ((j >> 1) & 1);
+ v[2] = v[1] * (1 - 2 * (j & 1));
+
+ for (int k = 0; k < 3; k++) {
+
+ if (i < 3)
+ face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ else
+ face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ }
+ }
+
+ //tri 1
+ ADD_VTX(0);
+ ADD_VTX(1);
+ ADD_VTX(2);
+ //tri 2
+ ADD_VTX(2);
+ ADD_VTX(3);
+ ADD_VTX(0);
+ }
+
+ arr[Mesh::ARRAY_VERTEX] = vertices;
+ arr[Mesh::ARRAY_COLOR] = colors;
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arr);
+ }
+
+ {
+ Ref<SpatialMaterial> fsm;
+ fsm.instance();
+ fsm->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
+ fsm->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ fsm->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
+ fsm->set_albedo(Color(1, 1, 1, 1));
+
+ mesh->surface_set_material(0, fsm);
+ }
+
+ mm->set_mesh(mesh);
+
+ int idx = 0;
+ _debug_mesh(0, 0, po2_bounds, mm, idx, p_mode);
+
+ return mm;
+}
+
+struct VoxelLightBakerOctree {
+
+ enum {
+ CHILD_EMPTY = 0xFFFFFFFF
+ };
+
+ uint16_t light[6][3]; //anisotropic light
+ float alpha;
+ uint32_t children[8];
+};
+
+PoolVector<uint8_t> VoxelLightBaker::create_capture_octree(int p_subdiv) {
+
+ p_subdiv = MIN(p_subdiv, cell_subdiv); // use the smaller one
+
+ Vector<uint32_t> remap;
+ int bc = bake_cells.size();
+ remap.resize(bc);
+ Vector<uint32_t> demap;
+
+ int new_size = 0;
+ for (int i = 0; i < bc; i++) {
+ uint32_t c = CHILD_EMPTY;
+ if (bake_cells[i].level < p_subdiv) {
+ c = new_size;
+ new_size++;
+ demap.push_back(i);
+ }
+ remap[i] = c;
+ }
+
+ Vector<VoxelLightBakerOctree> octree;
+ octree.resize(new_size);
+
+ for (int i = 0; i < new_size; i++) {
+ octree[i].alpha = bake_cells[demap[i]].alpha;
+ for (int j = 0; j < 6; j++) {
+ for (int k = 0; k < 3; k++) {
+ float l = bake_light[demap[i]].accum[j][k]; //add anisotropic light
+ l += bake_cells[demap[i]].emission[k]; //add emission
+ octree[i].light[j][k] = CLAMP(l * 1024, 0, 65535); //give two more bits to octree
+ }
+ }
+
+ for (int j = 0; j < 8; j++) {
+ uint32_t child = bake_cells[demap[i]].childs[j];
+ octree[i].children[j] = child == CHILD_EMPTY ? CHILD_EMPTY : remap[child];
+ }
+ }
+
+ PoolVector<uint8_t> ret;
+ int ret_bytes = octree.size() * sizeof(VoxelLightBakerOctree);
+ ret.resize(ret_bytes);
+ {
+ PoolVector<uint8_t>::Write w = ret.write();
+ copymem(w.ptr(), octree.ptr(), ret_bytes);
+ }
+
+ return ret;
+}
+
+float VoxelLightBaker::get_cell_size() const {
+ return cell_size;
+}
+
+Transform VoxelLightBaker::get_to_cell_space_xform() const {
+ return to_cell_space;
+}
+VoxelLightBaker::VoxelLightBaker() {
+ color_scan_cell_width = 4;
+ bake_texture_size = 128;
+ propagation = 0.85;
+ energy = 1.0;
+}
diff --git a/scene/3d/voxel_light_baker.h b/scene/3d/voxel_light_baker.h
new file mode 100644
index 0000000000..6dee2ee69b
--- /dev/null
+++ b/scene/3d/voxel_light_baker.h
@@ -0,0 +1,148 @@
+#ifndef VOXEL_LIGHT_BAKER_H
+#define VOXEL_LIGHT_BAKER_H
+
+#include "scene/3d/mesh_instance.h"
+#include "scene/resources/multimesh.h"
+
+class VoxelLightBaker {
+public:
+ enum DebugMode {
+ DEBUG_ALBEDO,
+ DEBUG_LIGHT
+ };
+
+ enum BakeQuality {
+ BAKE_QUALITY_LOW,
+ BAKE_QUALITY_MEDIUM,
+ BAKE_QUALITY_HIGH
+ };
+
+ enum BakeMode {
+ BAKE_MODE_CONE_TRACE,
+ BAKE_MODE_RAY_TRACE,
+ };
+
+private:
+ enum {
+ CHILD_EMPTY = 0xFFFFFFFF
+
+ };
+
+ struct Cell {
+
+ uint32_t childs[8];
+ float albedo[3]; //albedo in RGB24
+ float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast)
+ float normal[3];
+ uint32_t used_sides;
+ float alpha; //used for upsampling
+ int level;
+
+ Cell() {
+ for (int i = 0; i < 8; i++) {
+ childs[i] = CHILD_EMPTY;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ emission[i] = 0;
+ albedo[i] = 0;
+ normal[i] = 0;
+ }
+ alpha = 0;
+ used_sides = 0;
+ level = 0;
+ }
+ };
+
+ Vector<Cell> bake_cells;
+ int cell_subdiv;
+
+ struct Light {
+ int x, y, z;
+ float accum[6][3]; //rgb anisotropic
+ float direct_accum[6][3]; //for direct bake
+ int next_leaf;
+ };
+
+ int first_leaf;
+
+ Vector<Light> bake_light;
+
+ struct MaterialCache {
+ //128x128 textures
+ Vector<Color> albedo;
+ Vector<Color> emission;
+ };
+
+ Map<Ref<Material>, MaterialCache> material_cache;
+ int leaf_voxel_count;
+ bool direct_lights_baked;
+
+ AABB original_bounds;
+ AABB po2_bounds;
+ int axis_cell_size[3];
+
+ Transform to_cell_space;
+
+ int color_scan_cell_width;
+ int bake_texture_size;
+ float cell_size;
+ float propagation;
+ float energy;
+
+ BakeQuality bake_quality;
+ BakeMode bake_mode;
+
+ int max_original_cells;
+
+ void _init_light_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, uint32_t p_parent);
+
+ Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add);
+ MaterialCache _get_material_cache(Ref<Material> p_material);
+ void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const MaterialCache &p_material, const AABB &p_aabb);
+ void _fixup_plot(int p_idx, int p_level);
+ void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx, DebugMode p_mode);
+ void _check_init_light();
+
+ uint32_t _find_cell_at_pos(const Cell *cells, int x, int y, int z);
+
+ struct LightMap {
+ Vector3 light;
+ Vector3 pos;
+ Vector3 normal;
+ };
+
+ void _plot_triangle(Vector2 *vertices, Vector3 *positions, Vector3 *normals, LightMap *pixels, int width, int height);
+
+ _FORCE_INLINE_ void _sample_baked_octree_filtered_and_anisotropic(const Vector3 &p_posf, const Vector3 &p_direction, float p_level, Vector3 &r_color, float &r_alpha);
+ _FORCE_INLINE_ Vector3 _voxel_cone_trace(const Vector3 &p_pos, const Vector3 &p_normal, float p_aperture);
+ _FORCE_INLINE_ Vector3 _compute_pixel_light_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
+ _FORCE_INLINE_ Vector3 _compute_ray_trace_at_pos(const Vector3 &p_pos, const Vector3 &p_normal);
+
+public:
+ void begin_bake(int p_subdiv, const AABB &p_bounds);
+ void plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material);
+ void begin_bake_light(BakeQuality p_quality = BAKE_QUALITY_MEDIUM, BakeMode p_bake_mode = BAKE_MODE_CONE_TRACE, float p_propagation = 0.85, float p_energy = 1);
+ void plot_light_directional(const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, bool p_direct);
+ void plot_light_omni(const Vector3 &p_pos, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, bool p_direct);
+ void plot_light_spot(const Vector3 &p_pos, const Vector3 &p_axis, const Color &p_color, float p_energy, float p_indirect_energy, float p_radius, float p_attenutation, float p_spot_angle, float p_spot_attenuation, bool p_direct);
+ void end_bake();
+
+ struct LightMapData {
+ int width;
+ int height;
+ PoolVector<float> light;
+ };
+
+ Error make_lightmap(const Transform &p_xform, Ref<Mesh> &p_mesh, LightMapData &r_lightmap, bool (*p_bake_time_func)(void *, float, float) = NULL, void *p_bake_time_ud = NULL);
+
+ PoolVector<int> create_gi_probe_data();
+ Ref<MultiMesh> create_debug_multimesh(DebugMode p_mode = DEBUG_ALBEDO);
+ PoolVector<uint8_t> create_capture_octree(int p_subdiv);
+
+ float get_cell_size() const;
+ Transform get_to_cell_space_xform() const;
+ VoxelLightBaker();
+};
+
+#endif // VOXEL_LIGHT_BAKER_H
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 206f3ccca2..5e776c5a1a 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -233,7 +233,6 @@ void AnimationPlayer::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
- //stop_all();
clear_caches();
} break;
}
@@ -364,6 +363,9 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
if (!nc) // no node cache for this track, skip it
continue;
+ if (!a->track_is_enabled(i))
+ continue; // do nothing if the track is disabled
+
if (a->track_get_key_count(i) == 0)
continue; // do nothing if track is empty
@@ -735,7 +737,7 @@ void AnimationPlayer::remove_animation(const StringName &p_name) {
ERR_FAIL_COND(!animation_set.has(p_name));
- stop_all();
+ stop();
_unref_anim(animation_set[p_name].animation);
animation_set.erase(p_name);
@@ -772,9 +774,7 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
ERR_FAIL_COND(String(p_new_name).find("/") != -1 || String(p_new_name).find(":") != -1);
ERR_FAIL_COND(animation_set.has(p_new_name));
- //print_line("Rename anim: "+String(p_name)+" name: "+String(p_new_name));
-
- stop_all();
+ stop();
AnimationData ad = animation_set[p_name];
ad.name = p_new_name;
animation_set.erase(p_name);
@@ -1016,13 +1016,6 @@ void AnimationPlayer::stop(bool p_reset) {
playing = false;
}
-void AnimationPlayer::stop_all() {
-
- stop();
-
- _set_process(false); // always process when starting an animation
-}
-
void AnimationPlayer::set_speed_scale(float p_speed) {
speed_scale = p_speed;
@@ -1304,8 +1297,8 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(""), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(""), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("stop", "reset"), &AnimationPlayer::stop, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("stop_all"), &AnimationPlayer::stop_all);
ClassDB::bind_method(D_METHOD("is_playing"), &AnimationPlayer::is_playing);
+
ClassDB::bind_method(D_METHOD("set_current_animation", "anim"), &AnimationPlayer::set_current_animation);
ClassDB::bind_method(D_METHOD("get_current_animation"), &AnimationPlayer::get_current_animation);
ClassDB::bind_method(D_METHOD("queue", "name"), &AnimationPlayer::queue);
@@ -1323,9 +1316,6 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_root", "path"), &AnimationPlayer::set_root);
ClassDB::bind_method(D_METHOD("get_root"), &AnimationPlayer::get_root);
- ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("get_position"), &AnimationPlayer::get_current_animation_position);
-
ClassDB::bind_method(D_METHOD("find_animation", "animation"), &AnimationPlayer::find_animation);
ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationPlayer::clear_caches);
@@ -1336,6 +1326,7 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
+ ClassDB::bind_method(D_METHOD("seek", "seconds", "update"), &AnimationPlayer::seek, DEFVAL(false));
ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance);
ADD_GROUP("Playback Options", "playback_");
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 23eccec82f..32f82fe6b8 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -831,7 +831,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
for (List<AnimationNode::TrackRef>::Element *E = anim_list->tref.front(); E; E = E->next()) {
AnimationNode::TrackRef &tr = E->get();
- if (tr.track == NULL || tr.local_track < 0 || tr.weight < CMP_EPSILON)
+ if (tr.track == NULL || tr.local_track < 0 || tr.weight < CMP_EPSILON || !a->track_is_enabled(tr.local_track))
continue;
switch (a->track_get_type(tr.local_track)) {
@@ -1796,6 +1796,10 @@ void AnimationTreePlayer::_bind_methods() {
ADD_GROUP("Playback", "playback_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_animation_process_mode", "get_animation_process_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "master_player"), "set_master_player", "get_master_player");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "base_path"), "set_base_path", "get_base_path");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
+
BIND_ENUM_CONSTANT(NODE_OUTPUT);
BIND_ENUM_CONSTANT(NODE_ANIMATION);
BIND_ENUM_CONSTANT(NODE_ONESHOT);
diff --git a/scene/animation/tween_interpolaters.cpp b/scene/animation/tween_interpolaters.cpp
index 8f543a575a..cbf941f3ed 100644
--- a/scene/animation/tween_interpolaters.cpp
+++ b/scene/animation/tween_interpolaters.cpp
@@ -50,7 +50,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return c * t / d + b;
}
-};
+}; // namespace linear
///////////////////////////////////////////////////////////////////////////
// sine
///////////////////////////////////////////////////////////////////////////
@@ -70,7 +70,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace sine
///////////////////////////////////////////////////////////////////////////
// quint
///////////////////////////////////////////////////////////////////////////
@@ -92,7 +92,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace quint
///////////////////////////////////////////////////////////////////////////
// quart
///////////////////////////////////////////////////////////////////////////
@@ -114,7 +114,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace quart
///////////////////////////////////////////////////////////////////////////
// quad
///////////////////////////////////////////////////////////////////////////
@@ -137,7 +137,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace quad
///////////////////////////////////////////////////////////////////////////
// expo
///////////////////////////////////////////////////////////////////////////
@@ -163,7 +163,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace expo
///////////////////////////////////////////////////////////////////////////
// elastic
///////////////////////////////////////////////////////////////////////////
@@ -205,7 +205,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace elastic
///////////////////////////////////////////////////////////////////////////
// cubic
///////////////////////////////////////////////////////////////////////////
@@ -227,7 +227,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace cubic
///////////////////////////////////////////////////////////////////////////
// circ
///////////////////////////////////////////////////////////////////////////
@@ -248,7 +248,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace circ
///////////////////////////////////////////////////////////////////////////
// bounce
///////////////////////////////////////////////////////////////////////////
@@ -281,7 +281,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace bounce
///////////////////////////////////////////////////////////////////////////
// back
///////////////////////////////////////////////////////////////////////////
@@ -307,7 +307,7 @@ static real_t in_out(real_t t, real_t b, real_t c, real_t d) {
static real_t out_in(real_t t, real_t b, real_t c, real_t d) {
return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d);
}
-};
+}; // namespace back
Tween::interpolater Tween::interpolaters[Tween::TRANS_COUNT][Tween::EASE_COUNT] = {
{ &linear::in, &linear::out, &linear::in_out, &linear::out_in },
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index 4d00a2011f..ebbbbed1f9 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -70,8 +70,8 @@ class HBoxContainer : public BoxContainer {
GDCLASS(HBoxContainer, BoxContainer);
public:
- HBoxContainer()
- : BoxContainer(false) {}
+ HBoxContainer() :
+ BoxContainer(false) {}
};
class MarginContainer;
@@ -82,8 +82,8 @@ class VBoxContainer : public BoxContainer {
public:
MarginContainer *add_margin_child(const String &p_label, Control *p_control, bool p_expand = false);
- VBoxContainer()
- : BoxContainer(true) {}
+ VBoxContainer() :
+ BoxContainer(true) {}
};
VARIANT_ENUM_CAST(BoxContainer::AlignMode);
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 1fa03f81f4..47977f0283 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -55,6 +55,10 @@ Size2 Button::get_minimum_size() const {
return get_stylebox("normal")->get_minimum_size() + minsize;
}
+void Button::_set_internal_margin(Margin p_margin, float p_value) {
+ _internal_margin[p_margin] = p_value;
+}
+
void Button::_notification(int p_what) {
if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
@@ -136,11 +140,11 @@ void Button::_notification(int p_what) {
Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2();
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
- Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text)) / 2.0;
+ Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0;
switch (align) {
case ALIGN_LEFT: {
- text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x;
+ text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
text_ofs.y += style->get_offset().y;
} break;
case ALIGN_CENTER: {
@@ -150,7 +154,11 @@ void Button::_notification(int p_what) {
text_ofs += style->get_offset();
} break;
case ALIGN_RIGHT: {
- text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
+ if (_internal_margin[MARGIN_RIGHT] > 0) {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation");
+ } else {
+ text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
+ }
text_ofs.y += style->get_offset().y;
} break;
}
@@ -162,7 +170,11 @@ void Button::_notification(int p_what) {
int valign = size.height - style->get_minimum_size().y;
if (is_disabled())
color_icon.a = 0.4;
- _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
+ if (_internal_margin[MARGIN_LEFT] > 0) {
+ _icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
+ } else {
+ _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
+ }
}
}
}
@@ -253,7 +265,7 @@ void Button::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
}
Button::Button(const String &p_text) {
@@ -263,6 +275,10 @@ Button::Button(const String &p_text) {
set_mouse_filter(MOUSE_FILTER_STOP);
set_text(p_text);
align = ALIGN_CENTER;
+
+ for (int i = 0; i < 4; i++) {
+ _internal_margin[i] = 0;
+ }
}
Button::~Button() {
diff --git a/scene/gui/button.h b/scene/gui/button.h
index dd6e730b86..35488582de 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -53,9 +53,11 @@ private:
Ref<Texture> icon;
bool clip_text;
TextAlign align;
+ float _internal_margin[4];
protected:
virtual Size2 get_minimum_size() const;
+ void _set_internal_margin(Margin p_margin, float p_value);
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index e2b10a948f..bf8156b92b 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -31,18 +31,54 @@
#include "servers/visual_server.h"
+Size2 CheckBox::get_icon_size() const {
+ Ref<Texture> checked = Control::get_icon("checked");
+ Ref<Texture> unchecked = Control::get_icon("unchecked");
+ Ref<Texture> radio_checked = Control::get_icon("radio_checked");
+ Ref<Texture> radio_unchecked = Control::get_icon("radio_unchecked");
+
+ Size2 tex_size = Size2(0, 0);
+ if (!checked.is_null())
+ tex_size = Size2(checked->get_width(), checked->get_height());
+ if (!unchecked.is_null())
+ tex_size = Size2(MAX(tex_size.width, unchecked->get_width()), MAX(tex_size.height, unchecked->get_height()));
+ if (!radio_checked.is_null())
+ tex_size = Size2(MAX(tex_size.width, radio_checked->get_width()), MAX(tex_size.height, radio_checked->get_height()));
+ if (!radio_unchecked.is_null())
+ tex_size = Size2(MAX(tex_size.width, radio_unchecked->get_width()), MAX(tex_size.height, radio_unchecked->get_height()));
+ return tex_size;
+}
+
+Size2 CheckBox::get_minimum_size() const {
+
+ Size2 minsize = Button::get_minimum_size();
+ Size2 tex_size = get_icon_size();
+ minsize.width += tex_size.width;
+ if (get_text().length() > 0) {
+ minsize.width += get_constant("hseparation");
+ }
+ Ref<StyleBox> sb = get_stylebox("normal");
+ minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM));
+
+ return minsize;
+}
+
void CheckBox::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
+ if (p_what == NOTIFICATION_THEME_CHANGED) {
+
+ _set_internal_margin(MARGIN_LEFT, get_icon_size().width);
+ } else if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
Ref<Texture> on = Control::get_icon(is_radio() ? "radio_checked" : "checked");
Ref<Texture> off = Control::get_icon(is_radio() ? "radio_unchecked" : "unchecked");
+ Ref<StyleBox> sb = get_stylebox("normal");
Vector2 ofs;
- ofs.x = 0;
- ofs.y = int((get_size().height - on->get_height()) / 2);
+ ofs.x = sb->get_margin(MARGIN_LEFT);
+ ofs.y = int((get_size().height - get_icon_size().height) / 2);
if (is_pressed())
on->draw(ci, ofs);
@@ -56,10 +92,11 @@ bool CheckBox::is_radio() {
return get_button_group().is_valid();
}
-CheckBox::CheckBox(const String &p_text)
- : Button(p_text) {
+CheckBox::CheckBox(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
set_text_align(ALIGN_LEFT);
+ _set_internal_margin(MARGIN_LEFT, get_icon_size().width);
}
CheckBox::~CheckBox() {
diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h
index 4da06be8d1..3d3f170e8c 100644
--- a/scene/gui/check_box.h
+++ b/scene/gui/check_box.h
@@ -39,6 +39,8 @@ class CheckBox : public Button {
GDCLASS(CheckBox, Button);
protected:
+ Size2 get_icon_size() const;
+ Size2 get_minimum_size() const;
void _notification(int p_what);
bool is_radio();
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 77fdedd5e5..641d2d4f01 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -32,10 +32,7 @@
#include "print_string.h"
#include "servers/visual_server.h"
-Size2 CheckButton::get_minimum_size() const {
-
- Size2 minsize = Button::get_minimum_size();
-
+Size2 CheckButton::get_icon_size() const {
Ref<Texture> on = Control::get_icon("on");
Ref<Texture> off = Control::get_icon("off");
Size2 tex_size = Size2(0, 0);
@@ -43,15 +40,29 @@ Size2 CheckButton::get_minimum_size() const {
tex_size = Size2(on->get_width(), on->get_height());
if (!off.is_null())
tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height()));
- minsize += Size2(tex_size.width + get_constant("hseparation"), 0);
- minsize.height = MAX(minsize.height, tex_size.height);
+ return tex_size;
+}
+
+Size2 CheckButton::get_minimum_size() const {
- return get_stylebox("normal")->get_minimum_size() + minsize;
+ Size2 minsize = Button::get_minimum_size();
+ Size2 tex_size = get_icon_size();
+ minsize.width += tex_size.width;
+ if (get_text().length() > 0) {
+ minsize.width += get_constant("hseparation");
+ }
+ Ref<StyleBox> sb = get_stylebox("normal");
+ minsize.height = MAX(minsize.height, tex_size.height + sb->get_margin(MARGIN_TOP) + sb->get_margin(MARGIN_BOTTOM));
+
+ return minsize;
}
void CheckButton::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
+ if (p_what == NOTIFICATION_THEME_CHANGED) {
+
+ _set_internal_margin(MARGIN_RIGHT, get_icon_size().width);
+ } else if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
@@ -59,10 +70,11 @@ void CheckButton::_notification(int p_what) {
Ref<Texture> off = Control::get_icon("off");
Ref<StyleBox> sb = get_stylebox("normal");
- Size2 sb_ofs = Size2(sb->get_margin(MARGIN_RIGHT), sb->get_margin(MARGIN_TOP));
Vector2 ofs;
- ofs.x = get_minimum_size().width - (on->get_width() + sb_ofs.width);
- ofs.y = sb_ofs.height;
+ Size2 tex_size = get_icon_size();
+
+ ofs.x = get_size().width - (tex_size.width + sb->get_margin(MARGIN_RIGHT));
+ ofs.y = (get_size().height - tex_size.height) / 2;
if (is_pressed())
on->draw(ci, ofs);
@@ -75,6 +87,8 @@ CheckButton::CheckButton() {
set_toggle_mode(true);
set_text_align(ALIGN_LEFT);
+
+ _set_internal_margin(MARGIN_RIGHT, get_icon_size().width);
}
CheckButton::~CheckButton() {
diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h
index eb68943fe7..3103a40d3c 100644
--- a/scene/gui/check_button.h
+++ b/scene/gui/check_button.h
@@ -39,6 +39,7 @@ class CheckButton : public Button {
GDCLASS(CheckButton, Button);
protected:
+ Size2 get_icon_size() const;
virtual Size2 get_minimum_size() const;
void _notification(int p_what);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index dbd7c1bbc0..cb6283507e 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -486,8 +486,8 @@ void ColorPicker::_bind_methods() {
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
}
-ColorPicker::ColorPicker()
- : BoxContainer(true) {
+ColorPicker::ColorPicker() :
+ BoxContainer(true) {
updating = true;
edit_alpha = true;
@@ -664,11 +664,16 @@ ColorPicker *ColorPickerButton::get_picker() {
return picker;
}
+PopupPanel *ColorPickerButton::get_popup() {
+ return popup;
+}
+
void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPickerButton::get_pick_color);
ClassDB::bind_method(D_METHOD("get_picker"), &ColorPickerButton::get_picker);
+ ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
ClassDB::bind_method(D_METHOD("_color_changed"), &ColorPickerButton::_color_changed);
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 7de67a707c..c02cdc8608 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -130,6 +130,7 @@ public:
bool is_editing_alpha() const;
ColorPicker *get_picker();
+ PopupPanel *get_popup();
ColorPickerButton();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index adca78d1d4..81d2b6731f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -2470,6 +2470,16 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}
+void Control::set_pass_on_modal_close_click(bool p_pass_on) {
+
+ data.pass_on_modal_close_click = p_pass_on;
+}
+
+bool Control::pass_on_modal_close_click() const {
+
+ return data.pass_on_modal_close_click;
+}
+
Control *Control::get_focus_owner() const {
ERR_FAIL_COND_V(!is_inside_tree(), NULL);
@@ -2934,6 +2944,7 @@ Control::Control() {
data.parent = NULL;
data.mouse_filter = MOUSE_FILTER_STOP;
+ data.pass_on_modal_close_click = true;
data.SI = NULL;
data.MI = NULL;
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 92d1c969fc..9ac0eb0be3 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -165,6 +165,8 @@ private:
bool pending_min_size_update;
Point2 custom_minimum_size;
+ bool pass_on_modal_close_click;
+
MouseFilter mouse_filter;
bool clip_contents;
@@ -401,6 +403,9 @@ public:
void set_mouse_filter(MouseFilter p_filter);
MouseFilter get_mouse_filter() const;
+ void set_pass_on_modal_close_click(bool p_pass_on);
+ bool pass_on_modal_close_click() const;
+
/* SKINNING */
void add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index d4912339da..9a55073bb6 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -289,10 +289,17 @@ bool WindowDialog::get_resizable() const {
Size2 WindowDialog::get_minimum_size() const {
Ref<Font> font = get_font("title_font", "WindowDialog");
- int msx = close_button->get_combined_minimum_size().x;
- msx += font->get_string_size(title).x;
- return Size2(msx, 1);
+ const int button_width = close_button->get_combined_minimum_size().x;
+ const int title_width = font->get_string_size(title).x;
+ const int padding = button_width / 2;
+ const int button_area = button_width + padding;
+
+ // as the title gets centered, title_width + close_button_width is not enough.
+ // we want a width w, such that w / 2 - title_width / 2 >= button_area, i.e.
+ // w >= 2 * button_area + title_width
+
+ return Size2(2 * button_area + title_width, 1);
}
TextureButton *WindowDialog::get_close_button() {
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 8af499fd96..6af869c503 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -603,6 +603,14 @@ void FileDialog::set_current_path(const String &p_path) {
}
}
+void FileDialog::set_mode_overrides_title(bool p_override) {
+ mode_overrides_title = p_override;
+}
+
+bool FileDialog::is_mode_overriding_title() const {
+ return mode_overrides_title;
+}
+
void FileDialog::set_mode(Mode p_mode) {
mode = p_mode;
@@ -610,27 +618,32 @@ void FileDialog::set_mode(Mode p_mode) {
case MODE_OPEN_FILE:
get_ok()->set_text(RTR("Open"));
- set_title(RTR("Open a File"));
+ if (mode_overrides_title)
+ set_title(RTR("Open a File"));
makedir->hide();
break;
case MODE_OPEN_FILES:
get_ok()->set_text(RTR("Open"));
- set_title(RTR("Open File(s)"));
+ if (mode_overrides_title)
+ set_title(RTR("Open File(s)"));
makedir->hide();
break;
case MODE_OPEN_DIR:
get_ok()->set_text(RTR("Select Current Folder"));
- set_title(RTR("Open a Directory"));
+ if (mode_overrides_title)
+ set_title(RTR("Open a Directory"));
makedir->show();
break;
case MODE_OPEN_ANY:
get_ok()->set_text(RTR("Open"));
- set_title(RTR("Open a File or Directory"));
+ if (mode_overrides_title)
+ set_title(RTR("Open a File or Directory"));
makedir->show();
break;
case MODE_SAVE_FILE:
get_ok()->set_text(RTR("Save"));
- set_title(RTR("Save a File"));
+ if (mode_overrides_title)
+ set_title(RTR("Save a File"));
makedir->show();
break;
}
@@ -760,6 +773,8 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_current_dir", "dir"), &FileDialog::set_current_dir);
ClassDB::bind_method(D_METHOD("set_current_file", "file"), &FileDialog::set_current_file);
ClassDB::bind_method(D_METHOD("set_current_path", "path"), &FileDialog::set_current_path);
+ ClassDB::bind_method(D_METHOD("set_mode_overrides_title", "override"), &FileDialog::set_mode_overrides_title);
+ ClassDB::bind_method(D_METHOD("is_mode_overriding_title"), &FileDialog::is_mode_overriding_title);
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &FileDialog::set_mode);
ClassDB::bind_method(D_METHOD("get_mode"), &FileDialog::get_mode);
ClassDB::bind_method(D_METHOD("get_vbox"), &FileDialog::get_vbox);
@@ -791,6 +806,7 @@ void FileDialog::_bind_methods() {
BIND_ENUM_CONSTANT(ACCESS_USERDATA);
BIND_ENUM_CONSTANT(ACCESS_FILESYSTEM);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Open one,Open many,Open folder,Open any,Save"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access");
ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "filters"), "set_filters", "get_filters");
@@ -814,6 +830,8 @@ FileDialog::FileDialog() {
show_hidden_files = default_show_hidden_files;
+ mode_overrides_title = true;
+
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index ca3d9f54b2..c8c1f23105 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -92,6 +92,8 @@ private:
Vector<String> filters;
+ bool mode_overrides_title;
+
static bool default_show_hidden_files;
bool show_hidden_files;
@@ -143,6 +145,9 @@ public:
void set_current_file(const String &p_file);
void set_current_path(const String &p_path);
+ void set_mode_overrides_title(bool p_override);
+ bool is_mode_overriding_title() const;
+
void set_mode(Mode p_mode);
Mode get_mode() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index f7bf1cd9ea..85ae6d6241 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -1046,6 +1046,10 @@ void LineEdit::set_cursor_position(int p_pos) {
} else if (cursor_pos > window_pos) {
/* Adjust window if cursor goes too much to the right */
int window_width = get_size().width - style->get_minimum_size().width;
+ if (has_icon("right_icon")) {
+ Ref<Texture> r_icon = Control::get_icon("right_icon");
+ window_width -= r_icon->get_width();
+ }
if (window_width < 0)
return;
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index ac450616d6..d850553957 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -111,6 +111,7 @@ MenuButton::MenuButton() {
popup->hide();
add_child(popup);
popup->set_as_toplevel(true);
+ popup->set_pass_on_modal_close_click(false);
connect("button_up", popup, "call_deferred", make_binds("grab_click_focus"));
set_process_unhandled_key_input(true);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index b4d0799945..70f3d9ca83 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -323,6 +323,7 @@ OptionButton::OptionButton() {
popup = memnew(PopupMenu);
popup->hide();
popup->set_as_toplevel(true);
+ popup->set_pass_on_modal_close_click(false);
add_child(popup);
popup->connect("id_pressed", this, "_selected");
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index f3711b86b6..698676cc39 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -42,24 +42,6 @@ String PopupMenu::_get_accel_text(int p_item) const {
else if (items[p_item].accel)
return keycode_get_string(items[p_item].accel);
return String();
-
- /*
- String atxt;
- if (p_accel&KEY_MASK_SHIFT)
- atxt+="Shift+";
- if (p_accel&KEY_MASK_ALT)
- atxt+="Alt+";
- if (p_accel&KEY_MASK_CTRL)
- atxt+="Ctrl+";
- if (p_accel&KEY_MASK_META)
- atxt+="Meta+";
-
- p_accel&=KEY_CODE_MASK;
-
- atxt+=String::chr(p_accel).to_upper();
-
- return atxt;
-*/
}
Size2 PopupMenu::get_minimum_size() const {
@@ -136,7 +118,6 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
Ref<Font> font = get_font("font");
int vseparation = get_constant("vseparation");
- //int hseparation = get_constant("hseparation");
float font_h = font->get_height();
for (int i = 0; i < items.size(); i++) {
@@ -202,10 +183,10 @@ void PopupMenu::_activate_submenu(int over) {
void PopupMenu::_submenu_timeout() {
- if (mouse_over == submenu_over) {
+ if (mouse_over == submenu_over)
_activate_submenu(mouse_over);
- submenu_over = -1;
- }
+
+ submenu_over = -1;
}
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
@@ -230,6 +211,11 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
mouse_over = i;
update();
+
+ if (items[i].submenu != "" && submenu_over != i) {
+ submenu_over = i;
+ submenu_timer->start();
+ }
break;
}
}
@@ -245,6 +231,11 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
mouse_over = i;
update();
+
+ if (items[i].submenu != "" && submenu_over != i) {
+ submenu_over = i;
+ submenu_timer->start();
+ }
break;
}
}
@@ -500,6 +491,13 @@ void PopupMenu::_notification(int p_what) {
} break;
case NOTIFICATION_MOUSE_EXIT: {
+ if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) {
+ mouse_over = -1;
+ update();
+ }
+ } break;
+ case NOTIFICATION_POPUP_HIDE: {
+
if (mouse_over >= 0) {
mouse_over = -1;
update();
@@ -624,6 +622,20 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
update();
}
+void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
+
+ Item item;
+ item.text = p_label;
+ item.xl_text = tr(p_label);
+ item.accel = p_accel;
+ item.ID = p_ID;
+ item.checkable = false;
+ item.max_states = p_max_states;
+ item.state = p_default_state;
+ items.push_back(item);
+ update();
+}
+
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -772,6 +784,11 @@ Ref<ShortCut> PopupMenu::get_item_shortcut(int p_idx) const {
return items[p_idx].shortcut;
}
+int PopupMenu::get_item_state(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), -1);
+ return items[p_idx].state;
+}
+
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -820,6 +837,27 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
update();
}
+void PopupMenu::set_item_multistate(int p_idx, int p_state) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ items[p_idx].state = p_state;
+ update();
+}
+
+void PopupMenu::toggle_item_multistate(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ if (0 >= items[p_idx].max_states) {
+ return;
+ }
+
+ ++items[p_idx].state;
+ if (items[p_idx].max_states <= items[p_idx].state)
+ items[p_idx].state = 0;
+
+ update();
+}
+
bool PopupMenu::is_item_checkable(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
return items[p_idx].checkable;
@@ -895,21 +933,34 @@ void PopupMenu::activate_item(int p_item) {
while (pop) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if ((items[p_item].checkable && hide_on_checkable_item_selection && pop->is_hide_on_checkable_item_selection()) || (!items[p_item].checkable && hide_on_item_selection && pop->is_hide_on_item_selection())) {
- pop->hide();
- next = next->get_parent();
- pop = Object::cast_to<PopupMenu>(next);
- } else {
- // Break out of loop when the next parent has
- // hide_on_item_selection disabled
+
+ if (items[p_item].checkable) {
+ if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection())
+ break;
+ } else if (0 < items[p_item].max_states) {
+ if (!hide_on_multistate_item_selection || !pop->is_hide_on_multistate_item_selection())
+ break;
+ } else if (!hide_on_item_selection || !pop->is_hide_on_item_selection())
break;
- }
+
+ pop->hide();
+ next = next->get_parent();
+ pop = Object::cast_to<PopupMenu>(next);
}
+
// Hides popup by default; unless otherwise specified
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
- if ((items[p_item].checkable && hide_on_checkable_item_selection) || (!items[p_item].checkable && hide_on_item_selection)) {
- hide();
- }
+
+ if (items[p_item].checkable) {
+ if (!hide_on_checkable_item_selection)
+ return;
+ } else if (0 < items[p_item].max_states) {
+ if (!hide_on_multistate_item_selection)
+ return;
+ } else if (!hide_on_item_selection)
+ return;
+
+ hide();
}
void PopupMenu::remove_item(int p_idx) {
@@ -1025,7 +1076,7 @@ void PopupMenu::set_hide_on_item_selection(bool p_enabled) {
hide_on_item_selection = p_enabled;
}
-bool PopupMenu::is_hide_on_item_selection() {
+bool PopupMenu::is_hide_on_item_selection() const {
return hide_on_item_selection;
}
@@ -1035,11 +1086,21 @@ void PopupMenu::set_hide_on_checkable_item_selection(bool p_enabled) {
hide_on_checkable_item_selection = p_enabled;
}
-bool PopupMenu::is_hide_on_checkable_item_selection() {
+bool PopupMenu::is_hide_on_checkable_item_selection() const {
return hide_on_checkable_item_selection;
}
+void PopupMenu::set_hide_on_multistate_item_selection(bool p_enabled) {
+
+ hide_on_multistate_item_selection = p_enabled;
+}
+
+bool PopupMenu::is_hide_on_multistate_item_selection() const {
+
+ return hide_on_multistate_item_selection;
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1098,8 +1159,10 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
ClassDB::bind_method(D_METHOD("toggle_item_checked", "idx"), &PopupMenu::toggle_item_checked);
+ ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate);
ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &PopupMenu::get_item_text);
ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &PopupMenu::get_item_icon);
@@ -1131,6 +1194,9 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_checkable_item_selection", "enable"), &PopupMenu::set_hide_on_checkable_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_checkable_item_selection"), &PopupMenu::is_hide_on_checkable_item_selection);
+ ClassDB::bind_method(D_METHOD("set_hide_on_state_item_selection", "enable"), &PopupMenu::set_hide_on_multistate_item_selection);
+ ClassDB::bind_method(D_METHOD("is_hide_on_state_item_selection"), &PopupMenu::is_hide_on_multistate_item_selection);
+
ClassDB::bind_method(D_METHOD("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items");
@@ -1149,11 +1215,13 @@ void PopupMenu::set_invalidate_click_until_motion() {
PopupMenu::PopupMenu() {
mouse_over = -1;
+ submenu_over = -1;
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
set_hide_on_checkable_item_selection(true);
+ set_hide_on_multistate_item_selection(false);
submenu_timer = memnew(Timer);
submenu_timer->set_wait_time(0.3);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index c9e9c8e311..ee514f4c4b 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -46,6 +46,8 @@ class PopupMenu : public Popup {
String xl_text;
bool checked;
bool checkable;
+ int max_states;
+ int state;
bool separator;
bool disabled;
int ID;
@@ -62,6 +64,8 @@ class PopupMenu : public Popup {
checked = false;
checkable = false;
separator = false;
+ max_states = 0;
+ state = 0;
accel = 0;
disabled = false;
_ofs_cache = 0;
@@ -86,6 +90,7 @@ class PopupMenu : public Popup {
bool invalidated_click;
bool hide_on_item_selection;
bool hide_on_checkable_item_selection;
+ bool hide_on_multistate_item_selection;
Vector2 moved;
Array _get_items() const;
@@ -115,6 +120,8 @@ public:
void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
+ void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
+
void set_item_text(int p_idx, const String &p_text);
void set_item_icon(int p_idx, const Ref<Texture> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
@@ -128,6 +135,8 @@ public:
void set_item_tooltip(int p_idx, const String &p_tooltip);
void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
+ void set_item_multistate(int p_idx, int p_state);
+ void toggle_item_multistate(int p_idx);
void toggle_item_checked(int p_idx);
@@ -145,6 +154,7 @@ public:
bool is_item_checkable(int p_idx) const;
String get_item_tooltip(int p_idx) const;
Ref<ShortCut> get_item_shortcut(int p_idx) const;
+ int get_item_state(int p_idx) const;
int get_item_count() const;
@@ -168,10 +178,13 @@ public:
void set_invalidate_click_until_motion();
void set_hide_on_item_selection(bool p_enabled);
- bool is_hide_on_item_selection();
+ bool is_hide_on_item_selection() const;
void set_hide_on_checkable_item_selection(bool p_enabled);
- bool is_hide_on_checkable_item_selection();
+ bool is_hide_on_checkable_item_selection() const;
+
+ void set_hide_on_multistate_item_selection(bool p_enabled);
+ bool is_hide_on_multistate_item_selection() const;
PopupMenu();
~PopupMenu();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 124c268c8a..6fbc58a38a 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -31,6 +31,11 @@
#include "os/keyboard.h"
#include "os/os.h"
#include "scene/scene_string_names.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif
+
RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
if (p_free) {
@@ -370,7 +375,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Color uc = color;
uc.a *= 0.5;
int uy = y + lh - fh + ascent + 2;
- VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + pofs, uy), p_ofs + Point2(align_ofs + pofs + cw, uy), uc);
+ float underline_width = 1.0;
+#ifdef TOOLS_ENABLED
+ underline_width *= EDSCALE;
+#endif
+ VS::get_singleton()->canvas_item_add_line(ci, p_ofs + Point2(align_ofs + pofs, uy), p_ofs + Point2(align_ofs + pofs + cw, uy), uc, underline_width);
}
ofs += cw;
}
@@ -453,7 +462,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
_process_line(frame, Point2(), ly, p_width, i, PROCESS_CACHE, cfont, Color());
- table->columns[column].min_width = MAX(table->columns[i].min_width, frame->lines[i].minimum_width);
+ table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
}
idx++;
}
@@ -793,6 +802,17 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
selection.click = item;
selection.click_char = line;
+
+ // Erase previous selection.
+ if (selection.active) {
+ selection.from = NULL;
+ selection.from_char = NULL;
+ selection.to = NULL;
+ selection.to_char = NULL;
+ selection.active = false;
+
+ update();
+ }
}
}
@@ -1811,7 +1831,7 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
void RichTextLabel::selection_copy() {
- if (!selection.enabled)
+ if (!selection.active || !selection.enabled)
return;
String text;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 1096e3f650..b9a719dd10 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -256,8 +256,8 @@ private:
Item *to;
int to_char;
- bool active;
- bool enabled;
+ bool active; // anything selected? i.e. from, to, etc. valid?
+ bool enabled; // allow selections?
};
Selection selection;
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 373483a8a0..753bd35de7 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -117,8 +117,8 @@ class HScrollBar : public ScrollBar {
GDCLASS(HScrollBar, ScrollBar);
public:
- HScrollBar()
- : ScrollBar(HORIZONTAL) { set_v_size_flags(0); }
+ HScrollBar() :
+ ScrollBar(HORIZONTAL) { set_v_size_flags(0); }
};
class VScrollBar : public ScrollBar {
@@ -126,8 +126,8 @@ class VScrollBar : public ScrollBar {
GDCLASS(VScrollBar, ScrollBar);
public:
- VScrollBar()
- : ScrollBar(VERTICAL) { set_h_size_flags(0); }
+ VScrollBar() :
+ ScrollBar(VERTICAL) { set_h_size_flags(0); }
};
#endif
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index a71a1c5f92..ffe0db691f 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -354,6 +354,7 @@ void ScrollContainer::update_scrollbars() {
if (!scroll_v || min.height <= size.height - hmin.height) {
v_scroll->hide();
+ v_scroll->set_max(0);
scroll.y = 0;
} else {
@@ -366,6 +367,7 @@ void ScrollContainer::update_scrollbars() {
if (!scroll_h || min.width <= size.width - vmin.width) {
h_scroll->hide();
+ h_scroll->set_max(0);
scroll.x = 0;
} else {
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index e88742a3e3..70b8616af1 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -171,47 +171,48 @@ void Slider::_notification(int p_what) {
Ref<StyleBox> grabber_area = get_stylebox("grabber_area");
Ref<Texture> grabber = get_icon(editable ? ((mouse_inside || has_focus()) ? "grabber_highlight" : "grabber") : "grabber_disabled");
Ref<Texture> tick = get_icon("tick");
+ double ratio = Math::is_nan(get_as_ratio()) ? 0 : get_as_ratio();
if (orientation == VERTICAL) {
int widget_width = style->get_minimum_size().width + style->get_center_size().width;
float areasize = size.height - grabber->get_size().height;
style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height)));
- grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * get_as_ratio() - grabber->get_size().height / 2), Size2i(widget_width, areasize * get_as_ratio() + grabber->get_size().width / 2)));
+ grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().width / 2)));
/*
if (mouse_inside||has_focus())
focus->draw(ci,Rect2i(Point2i(),Size2i(style->get_minimum_size().width+style->get_center_size().width,size.height)));
*/
if (ticks > 1) {
- int tickarea = size.height - tick->get_height();
+ int grabber_offset = (grabber->get_size().height / 2 - tick->get_height() / 2);
for (int i = 0; i < ticks; i++) {
if (!ticks_on_borders && (i == 0 || i + 1 == ticks)) continue;
- int ofs = i * tickarea / (ticks - 1);
+ int ofs = (i * areasize / (ticks - 1)) + grabber_offset;
tick->draw(ci, Point2i((size.width - widget_width) / 2, ofs));
}
}
- grabber->draw(ci, Point2i(size.width / 2 - grabber->get_size().width / 2, size.height - get_as_ratio() * areasize - grabber->get_size().height));
+ grabber->draw(ci, Point2i(size.width / 2 - grabber->get_size().width / 2, size.height - ratio * areasize - grabber->get_size().height));
} else {
int widget_height = style->get_minimum_size().height + style->get_center_size().height;
float areasize = size.width - grabber->get_size().width;
style->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(size.width, widget_height)));
- grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * get_as_ratio() + grabber->get_size().width / 2, widget_height)));
+ grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * ratio + grabber->get_size().width / 2, widget_height)));
/*
if (mouse_inside||has_focus())
focus->draw(ci,Rect2i(Point2i(),Size2i(size.width,style->get_minimum_size().height+style->get_center_size().height)));
*/
if (ticks > 1) {
- int tickarea = size.width - tick->get_width();
+ int grabber_offset = (grabber->get_size().width / 2 - tick->get_width() / 2);
for (int i = 0; i < ticks; i++) {
if ((!ticks_on_borders) && ((i == 0) || ((i + 1) == ticks))) continue;
- int ofs = i * tickarea / (ticks - 1);
+ int ofs = (i * areasize / (ticks - 1)) + grabber_offset;
tick->draw(ci, Point2i(ofs, (size.height - widget_height) / 2));
}
}
- grabber->draw(ci, Point2i(get_as_ratio() * areasize, size.height / 2 - grabber->get_size().height / 2));
+ grabber->draw(ci, Point2i(ratio * areasize, size.height / 2 - grabber->get_size().height / 2));
}
} break;
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index a2334a69fc..95ae429d63 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -77,8 +77,8 @@ class HSlider : public Slider {
GDCLASS(HSlider, Slider);
public:
- HSlider()
- : Slider(HORIZONTAL) { set_v_size_flags(0); }
+ HSlider() :
+ Slider(HORIZONTAL) { set_v_size_flags(0); }
};
class VSlider : public Slider {
@@ -86,8 +86,8 @@ class VSlider : public Slider {
GDCLASS(VSlider, Slider);
public:
- VSlider()
- : Slider(VERTICAL) { set_h_size_flags(0); }
+ VSlider() :
+ Slider(VERTICAL) { set_h_size_flags(0); }
};
#endif // SLIDER_H
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 8ec21b5eaa..c7a484c4c5 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -87,8 +87,8 @@ class HSplitContainer : public SplitContainer {
GDCLASS(HSplitContainer, SplitContainer);
public:
- HSplitContainer()
- : SplitContainer(false) { set_default_cursor_shape(CURSOR_HSPLIT); }
+ HSplitContainer() :
+ SplitContainer(false) { set_default_cursor_shape(CURSOR_HSPLIT); }
};
class VSplitContainer : public SplitContainer {
@@ -96,8 +96,8 @@ class VSplitContainer : public SplitContainer {
GDCLASS(VSplitContainer, SplitContainer);
public:
- VSplitContainer()
- : SplitContainer(true) { set_default_cursor_shape(CURSOR_VSPLIT); }
+ VSplitContainer() :
+ SplitContainer(true) { set_default_cursor_shape(CURSOR_VSPLIT); }
};
#endif // SPLIT_CONTAINER_H
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 581034ddee..f2a2d862de 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -294,13 +294,17 @@ void TabContainer::_notification(int p_what) {
}
} break;
case NOTIFICATION_THEME_CHANGED: {
- if (get_tab_count() > 0) {
- call_deferred("set_current_tab", get_current_tab()); //wait until all changed theme
- }
+ call_deferred("_on_theme_changed"); //wait until all changed theme
} break;
}
}
+void TabContainer::_on_theme_changed() {
+ if (get_tab_count() > 0) {
+ set_current_tab(get_current_tab());
+ }
+}
+
int TabContainer::_get_tab_width(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
@@ -658,6 +662,7 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
+ ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index be59a16b3f..a36c4f3790 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -60,6 +60,7 @@ private:
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
+ void _on_theme_changed();
protected:
void _child_renamed_callback();
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 6fa73e4b58..2ce709732c 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -251,13 +251,14 @@ void TextEdit::Text::clear() {
insert(0, "");
}
-int TextEdit::Text::get_max_width() const {
+int TextEdit::Text::get_max_width(bool p_exclude_hidden) const {
//quite some work.. but should be fast enough.
int max = 0;
-
- for (int i = 0; i < text.size(); i++)
- max = MAX(max, get_line_width(i));
+ for (int i = 0; i < text.size(); i++) {
+ if (!p_exclude_hidden || !is_hidden(i))
+ max = MAX(max, get_line_width(i));
+ }
return max;
}
@@ -307,7 +308,7 @@ void TextEdit::_update_scrollbars() {
int vscroll_pixels = v_scroll->get_combined_minimum_size().width;
int visible_width = size.width - cache.style_normal->get_minimum_size().width;
- int total_width = text.get_max_width() + vmin.x;
+ int total_width = text.get_max_width(true) + vmin.x;
if (line_numbers)
total_width += cache.line_number_w;
@@ -360,6 +361,7 @@ void TextEdit::_update_scrollbars() {
}
} else {
+
cursor.line_ofs = 0;
line_scroll_pos = 0;
v_scroll->set_value(0);
@@ -371,12 +373,16 @@ void TextEdit::_update_scrollbars() {
h_scroll->show();
h_scroll->set_max(total_width);
h_scroll->set_page(visible_width);
+ if (cursor.x_ofs > (total_width - visible_width))
+ cursor.x_ofs = (total_width - visible_width);
if (fabs(h_scroll->get_value() - (double)cursor.x_ofs) >= 1) {
h_scroll->set_value(cursor.x_ofs);
}
} else {
+ cursor.x_ofs = 0;
+ h_scroll->set_value(0);
h_scroll->hide();
}
@@ -594,6 +600,12 @@ void TextEdit::_notification(int p_what) {
int xmargin_end = cache.size.width - cache.style_normal->get_margin(MARGIN_RIGHT);
//let's do it easy for now:
cache.style_normal->draw(ci, Rect2(Point2(), cache.size));
+ float readonly_alpha = 1.0; // used to set the input text color when in read-only mode
+ if (readonly) {
+ cache.style_readonly->draw(ci, Rect2(Point2(), cache.size));
+ readonly_alpha = .5;
+ draw_caret = false;
+ }
if (has_focus())
cache.style_focus->draw(ci, Rect2(Point2(), cache.size));
@@ -604,6 +616,8 @@ void TextEdit::_notification(int p_what) {
int tab_w = cache.font->get_char_size(' ').width * indent_size;
Color color = cache.font_color;
+ color.a *= readonly_alpha;
+
int in_region = -1;
if (syntax_coloring) {
@@ -823,10 +837,16 @@ void TextEdit::_notification(int p_what) {
int char_margin = xmargin_beg - cursor.x_ofs;
int char_ofs = 0;
- int ofs_y = (i * get_row_height() + cache.line_spacing / 2);
- if (smooth_scroll_enabled) {
- ofs_y -= (v_scroll->get_value() - get_line_scroll_pos()) * get_row_height();
+
+ int ofs_readonly = 0;
+ int ofs_x = 0;
+ if (readonly) {
+ ofs_readonly = cache.style_readonly->get_offset().y / 2;
+ ofs_x = cache.style_readonly->get_offset().x / 2;
}
+ int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
+ if (smooth_scroll_enabled)
+ ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
bool prev_is_char = false;
bool prev_is_number = false;
@@ -852,27 +872,32 @@ void TextEdit::_notification(int p_what) {
if (text.is_marked(line)) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
}
if (str.length() == 0) {
// draw line background if empty as we won't loop at at all
if (line == cursor.line && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
}
// give visual indication of empty selected line
if (selection.active && line >= selection.from_line && line <= selection.to_line) {
int char_w = cache.font->get_char_size(' ').width;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, char_w, get_row_height()), cache.selection_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
+ }
+ } else {
+ // if it has text, then draw current line marker in the margin, as line number ect will draw over it, draw the rest of line marker later.
+ if (line == cursor.line && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color);
}
}
if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
#ifdef TOOLS_ENABLED
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
#else
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
#endif
}
@@ -895,11 +920,11 @@ void TextEdit::_notification(int p_what) {
if (is_folded(line)) {
int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
- cache.folded_icon->draw(ci, Point2(gutter_left + xofs, ofs_y + yofs), Color(0.8f, 0.8f, 0.8f, 0.8f));
+ cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
} else if (can_fold(line)) {
int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
- cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs, ofs_y + yofs), Color(0.8f, 0.8f, 0.8f, 0.8f));
+ cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
}
}
@@ -909,7 +934,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_y + 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, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
}
//loop through charcters in one line
for (int j = 0; j < str.length(); j++) {
@@ -924,6 +949,7 @@ void TextEdit::_notification(int p_what) {
if (syntax_coloring && deregion == 0) {
color = cache.font_color; //reset
+ color.a *= readonly_alpha;
//find keyword
bool is_char = _is_text_char(str[j]);
bool is_symbol = _is_symbol(str[j]);
@@ -1105,34 +1131,30 @@ void TextEdit::_notification(int p_what) {
bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column));
if (line == cursor.line && highlight_current_line) {
- // if its the first char draw behind line numbers
- if (j == 0) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
- }
// if its the last char draw to end of the line
if (j == str.length() - 1) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
}
// actual text
if (!in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
}
}
if (in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
}
if (in_search_result) {
Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, 1)), border_color);
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
if (j == search_text_col)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(1, get_row_height())), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color);
if (j == search_text_col + search_text.length() - 1)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w - 1, ofs_y), Size2i(1, get_row_height())), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
}
if (highlight_all_occurrences) {
@@ -1151,7 +1173,7 @@ void TextEdit::_notification(int p_what) {
}
if (in_highlighted_word) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
}
}
}
@@ -1162,7 +1184,7 @@ void TextEdit::_notification(int p_what) {
if (brace_open_mismatch)
color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
if (
@@ -1171,13 +1193,13 @@ void TextEdit::_notification(int p_what) {
if (brace_close_mismatch)
color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
if (cursor.column == j && cursor.line == line) {
- cursor_pos = Point2i(char_ofs + char_margin, ofs_y);
+ cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
if (insert_mode) {
cursor_pos.y += (get_row_height() - 3);
@@ -1204,7 +1226,7 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
}
- cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
+ cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
char_ofs += im_char_width;
ofs++;
@@ -1227,18 +1249,19 @@ void TextEdit::_notification(int p_what) {
color = cache.caret_background_color;
} else if (!syntax_coloring && block_caret) {
color = cache.font_color;
+ color.a *= readonly_alpha;
}
if (str[j] >= 32) {
- int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
}
else if (draw_tabs && str[j] == '\t') {
int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
char_ofs += char_w;
@@ -1246,13 +1269,15 @@ void TextEdit::_notification(int p_what) {
if (j == str.length() - 1 && is_folded(line)) {
int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2;
int xofs = cache.folded_eol_icon->get_width() / 2;
- cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs, ofs_y + yofs), Color(1, 1, 1, 1));
+ Color eol_color = cache.code_folding_color;
+ eol_color.a = 1;
+ cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
}
}
if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) {
- cursor_pos = Point2i(char_ofs + char_margin, ofs_y);
+ cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
if (insert_mode) {
cursor_pos.y += (get_row_height() - 3);
@@ -1277,7 +1302,7 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
}
- cache.font->draw_char(ci, Point2(char_ofs + char_margin, ofs_y + ascent), cchar, next, color);
+ cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
char_ofs += im_char_width;
ofs++;
@@ -1639,17 +1664,22 @@ void TextEdit::backspace_at_cursor() {
cursor_set_column(prev_column);
}
-void TextEdit::indent_selection_right() {
+void TextEdit::indent_right() {
- if (!is_selection_active()) {
- return;
- }
+ int start_line;
+ int end_line;
begin_complex_operation();
- int start_line = get_selection_from_line();
- int end_line = get_selection_to_line();
+
+ if (is_selection_active()) {
+ start_line = get_selection_from_line();
+ end_line = get_selection_to_line();
+ } else {
+ start_line = cursor.line;
+ end_line = start_line;
+ }
// ignore if the cursor is not past the first column
- if (get_selection_to_column() == 0) {
+ if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
@@ -1663,23 +1693,32 @@ void TextEdit::indent_selection_right() {
set_line(i, line_text);
}
- // fix selection being off by one on the last line
- selection.to_column++;
+ // fix selection and cursor being off by one on the last line
+ if (is_selection_active()) {
+ selection.to_column++;
+ selection.from_column++;
+ }
+ cursor.column++;
end_complex_operation();
update();
}
-void TextEdit::indent_selection_left() {
+void TextEdit::indent_left() {
- if (!is_selection_active()) {
- return;
- }
+ int start_line;
+ int end_line;
begin_complex_operation();
- int start_line = get_selection_from_line();
- int end_line = get_selection_to_line();
+
+ if (is_selection_active()) {
+ start_line = get_selection_from_line();
+ end_line = get_selection_to_line();
+ } else {
+ start_line = cursor.line;
+ end_line = start_line;
+ }
// ignore if the cursor is not past the first column
- if (get_selection_to_column() == 0) {
+ if (is_selection_active() && get_selection_to_column() == 0) {
end_line--;
}
String last_line_text = get_line(end_line);
@@ -1696,9 +1735,15 @@ void TextEdit::indent_selection_left() {
}
}
- // fix selection being off by one on the last line
- if (last_line_text != get_line(end_line) && selection.to_column > 0) {
- selection.to_column--;
+ // fix selection and cursor being off by one on the last line
+ if (is_selection_active() && last_line_text != get_line(end_line)) {
+ if (selection.to_column > 0)
+ selection.to_column--;
+ if (selection.from_column > 0)
+ selection.from_column--;
+ }
+ if (cursor.column > 0) {
+ cursor.column--;
}
end_complex_operation();
update();
@@ -2191,9 +2236,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
case KEY_TAB: {
if (k->get_shift()) {
- indent_selection_left();
+ indent_left();
} else {
- indent_selection_right();
+ indent_right();
}
dobreak = true;
accept_event();
@@ -2364,8 +2409,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (readonly)
break;
- if (selection.active) {
-
+ if (is_selection_active()) {
+ if (k->get_shift()) {
+ indent_left();
+ } else {
+ indent_right();
+ }
} else {
if (k->get_shift()) {
@@ -3913,6 +3962,7 @@ void TextEdit::clear() {
void TextEdit::set_readonly(bool p_readonly) {
readonly = p_readonly;
+ update();
}
bool TextEdit::is_readonly() const {
@@ -3950,6 +4000,7 @@ void TextEdit::_update_caches() {
cache.style_normal = get_stylebox("normal");
cache.style_focus = get_stylebox("focus");
+ cache.style_readonly = get_stylebox("read_only");
cache.completion_background_color = get_color("completion_background_color");
cache.completion_selected_color = get_color("completion_selected_color");
cache.completion_existing_color = get_color("completion_existing_color");
@@ -3969,6 +4020,7 @@ void TextEdit::_update_caches() {
cache.current_line_color = get_color("current_line_color");
cache.line_length_guideline_color = get_color("line_length_guideline_color");
cache.breakpoint_color = get_color("breakpoint_color");
+ cache.code_folding_color = get_color("code_folding_color");
cache.brace_mismatch_color = get_color("brace_mismatch_color");
cache.word_highlighted_color = get_color("word_highlighted_color");
cache.search_result_color = get_color("search_result_color");
@@ -4489,16 +4541,17 @@ int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const {
return num_total;
}
-int TextEdit::get_whitespace_level(int p_line) const {
+int TextEdit::get_indent_level(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
// counts number of tabs and spaces before line starts
+ int tab_count = 0;
int whitespace_count = 0;
int line_length = text[p_line].size();
for (int i = 0; i < line_length - 1; i++) {
if (text[p_line][i] == '\t') {
- whitespace_count++;
+ tab_count++;
} else if (text[p_line][i] == ' ') {
whitespace_count++;
} else if (text[p_line][i] == '#') {
@@ -4507,7 +4560,7 @@ int TextEdit::get_whitespace_level(int p_line) const {
break;
}
}
- return whitespace_count;
+ return tab_count + whitespace_count / indent_size;
}
bool TextEdit::can_fold(int p_line) const {
@@ -4524,12 +4577,12 @@ bool TextEdit::can_fold(int p_line) const {
if (is_line_hidden(p_line))
return false;
- int start_indent = get_whitespace_level(p_line);
+ int start_indent = get_indent_level(p_line);
for (int i = p_line + 1; i < text.size(); i++) {
if (text[i].size() == 0)
continue;
- int next_indent = get_whitespace_level(i);
+ int next_indent = get_indent_level(i);
if (next_indent > start_indent)
return true;
else
@@ -4558,22 +4611,20 @@ void TextEdit::fold_line(int p_line) {
return;
// hide lines below this one
- int start_indent = get_whitespace_level(p_line);
+ int start_indent = get_indent_level(p_line);
+ int last_line = start_indent;
for (int i = p_line + 1; i < text.size(); i++) {
- int cur_indent = get_whitespace_level(i);
- if (text[i].size() == 0 || cur_indent > start_indent) {
- set_line_as_hidden(i, true);
- } else {
- // exclude trailing empty lines
- for (int trail_i = i - 1; trail_i > p_line; trail_i--) {
- if (text[trail_i].size() == 0)
- set_line_as_hidden(trail_i, false);
- else
- break;
+ if (text[i].strip_edges().size() != 0) {
+ if (get_indent_level(i) > start_indent) {
+ last_line = i;
+ } else {
+ break;
}
- break;
}
}
+ for (int i = p_line + 1; i <= last_line; i++) {
+ set_line_as_hidden(i, true);
+ }
// fix selection
if (is_selection_active()) {
@@ -4619,6 +4670,16 @@ void TextEdit::unfold_line(int p_line) {
update();
}
+void TextEdit::toggle_fold_line(int p_line) {
+
+ ERR_FAIL_INDEX(p_line, text.size());
+
+ if (!is_folded(p_line))
+ fold_line(p_line);
+ else
+ unfold_line(p_line);
+}
+
int TextEdit::get_line_count() const {
return text.size();
@@ -5434,6 +5495,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("unhide_all_lines"), &TextEdit::unhide_all_lines);
ClassDB::bind_method(D_METHOD("fold_line", "line"), &TextEdit::fold_line);
ClassDB::bind_method(D_METHOD("unfold_line", "line"), &TextEdit::unfold_line);
+ ClassDB::bind_method(D_METHOD("toggle_fold_line", "line"), &TextEdit::toggle_fold_line);
ClassDB::bind_method(D_METHOD("can_fold", "line"), &TextEdit::can_fold);
ClassDB::bind_method(D_METHOD("is_folded", "line"), &TextEdit::is_folded);
@@ -5619,4 +5681,4 @@ TextEdit::TextEdit() {
}
TextEdit::~TextEdit() {
-}
+} \ No newline at end of file
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index bb9ca87d06..edef28cc25 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -78,6 +78,7 @@ class TextEdit : public Control {
Ref<Texture> folded_eol_icon;
Ref<StyleBox> style_normal;
Ref<StyleBox> style_focus;
+ Ref<StyleBox> style_readonly;
Ref<Font> font;
Color completion_background_color;
Color completion_selected_color;
@@ -95,6 +96,7 @@ class TextEdit : public Control {
Color selection_color;
Color mark_color;
Color breakpoint_color;
+ Color code_folding_color;
Color current_line_color;
Color line_length_guideline_color;
Color brace_mismatch_color;
@@ -158,7 +160,7 @@ class TextEdit : public Control {
void set_font(const Ref<Font> &p_font);
void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
int get_line_width(int p_line) const;
- int get_max_width() const;
+ int get_max_width(bool p_exclude_hidden = false) const;
const Map<int, ColorRegionInfo> &get_color_region_info(int p_line);
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
@@ -430,19 +432,20 @@ public:
void fold_all_lines();
void unhide_all_lines();
int num_lines_from(int p_line_from, int unhidden_amount) const;
- int get_whitespace_level(int p_line) const;
bool can_fold(int p_line) const;
bool is_folded(int p_line) const;
void fold_line(int p_line);
void unfold_line(int p_line);
+ void toggle_fold_line(int p_line);
String get_text();
String get_line(int line) const;
void set_line(int line, String new_text);
void backspace_at_cursor();
- void indent_selection_left();
- void indent_selection_right();
+ void indent_left();
+ void indent_right();
+ int get_indent_level(int p_line) const;
inline void set_scroll_pass_end_of_file(bool p_enabled) {
scroll_past_end_of_file_enabled = p_enabled;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index ab12d123ba..b5b42e8f29 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -30,6 +30,7 @@
#include "tree.h"
#include <limits.h>
+#include "math_funcs.h"
#include "os/input.h"
#include "os/keyboard.h"
#include "os/os.h"
@@ -37,6 +38,10 @@
#include "project_settings.h"
#include "scene/main/viewport.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif
+
void TreeItem::move_to_top() {
if (!parent || parent->childs == this)
@@ -1412,9 +1417,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (c->get_children() != NULL)
root_pos -= Point2i(cache.arrow->get_width(), 0);
+ float line_width = 1.0;
+#ifdef TOOLS_ENABLED
+ line_width *= EDSCALE;
+#endif
+
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
- VisualServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x, root_pos.y), cache.relationship_line_color);
- VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), parent_pos, cache.relationship_line_color);
+ VisualServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x - Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width);
+ VisualServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), parent_pos, cache.relationship_line_color, line_width);
}
int child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c);
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 672e893f1b..4afdb56f86 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -36,7 +36,6 @@ void HTTPRequest::_redirect_request(const String &p_new_url) {
Error HTTPRequest::_request() {
- //print_line("Requesting:\n\tURL: "+url+"\n\tString: "+request_string+"\n\tPort: "+itos(port)+"\n\tSSL: "+itos(use_ssl)+"\n\tValidate SSL: "+itos(validate_ssl));
return client->connect_to_host(url, port, use_ssl, validate_ssl);
}
@@ -54,37 +53,32 @@ Error HTTPRequest::_parse_url(const String &p_url) {
downloaded = 0;
redirections = 0;
- //print_line("1 url: "+url);
- if (url.begins_with("http://")) {
-
+ String url_lower = url.to_lower();
+ if (url_lower.begins_with("http://")) {
url = url.substr(7, url.length() - 7);
- //print_line("no SSL");
-
- } else if (url.begins_with("https://")) {
+ } else if (url_lower.begins_with("https://")) {
url = url.substr(8, url.length() - 8);
use_ssl = true;
port = 443;
- //print_line("yes SSL");
} else {
ERR_EXPLAIN("Malformed URL");
ERR_FAIL_V(ERR_INVALID_PARAMETER);
}
- //print_line("2 url: "+url);
+ if (url.length() < 1) {
+ ERR_EXPLAIN("URL too short");
+ ERR_FAIL_V(ERR_INVALID_PARAMETER);
+ }
int slash_pos = url.find("/");
if (slash_pos != -1) {
request_string = url.substr(slash_pos, url.length());
url = url.substr(0, slash_pos);
- //print_line("request string: "+request_string);
} else {
request_string = "/";
- //print_line("no request");
}
- //print_line("3 url: "+url);
-
int colon_pos = url.find(":");
if (colon_pos != -1) {
port = url.substr(colon_pos + 1, url.length()).to_int();
@@ -92,8 +86,6 @@ Error HTTPRequest::_parse_url(const String &p_url) {
ERR_FAIL_COND_V(port < 1 || port > 65535, ERR_INVALID_PARAMETER);
}
- //print_line("4 url: "+url);
-
return OK;
}
@@ -198,10 +190,8 @@ void HTTPRequest::cancel_request() {
}
client->close();
body.resize(0);
- //downloaded=0;
got_response = false;
response_code = -1;
- //body_len=-1;
request_sent = false;
requesting = false;
}
@@ -221,12 +211,12 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
response_headers.resize(0);
downloaded = 0;
for (List<String>::Element *E = rheaders.front(); E; E = E->next()) {
- //print_line("HEADER: "+E->get());
response_headers.push_back(E->get());
}
if (response_code == 301 || response_code == 302) {
- //redirect
+ // Handle redirect
+
if (max_redirects >= 0 && redirections >= max_redirects) {
call_deferred("_request_done", RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PoolByteArray());
@@ -242,15 +232,13 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
}
}
- //print_line("NEW LOCATION: "+new_request);
-
if (new_request != "") {
- //process redirect
+ // Process redirect
client->close();
- int new_redirs = redirections + 1; //because _request() will clear it
+ int new_redirs = redirections + 1; // Because _request() will clear it
Error err;
if (new_request.begins_with("http")) {
- //new url, request all again
+ // New url, request all again
err = _parse_url(new_request);
} else {
request_string = new_request;
@@ -258,7 +246,6 @@ bool HTTPRequest::_handle_response(bool *ret_value) {
err = _request();
- //print_line("new connection: "+itos(err));
if (err == OK) {
request_sent = false;
got_response = false;
@@ -280,11 +267,11 @@ bool HTTPRequest::_update_connection() {
switch (client->get_status()) {
case HTTPClient::STATUS_DISCONNECTED: {
call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
- return true; //end it, since it's doing something
+ return true; // End it, since it's doing something
} break;
case HTTPClient::STATUS_RESOLVING: {
client->poll();
- //must wait
+ // Must wait
return false;
} break;
case HTTPClient::STATUS_CANT_RESOLVE: {
@@ -294,9 +281,9 @@ bool HTTPRequest::_update_connection() {
} break;
case HTTPClient::STATUS_CONNECTING: {
client->poll();
- //must wait
+ // Must wait
return false;
- } break; //connecting to ip
+ } break; // Connecting to IP
case HTTPClient::STATUS_CANT_CONNECT: {
call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
@@ -309,7 +296,7 @@ bool HTTPRequest::_update_connection() {
if (!got_response) {
- //no body
+ // No body
bool ret_value;
@@ -320,16 +307,16 @@ bool HTTPRequest::_update_connection() {
return true;
}
if (got_response && body_len < 0) {
- //chunked transfer is done
+ // Chunked transfer is done
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
call_deferred("_request_done", RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PoolByteArray());
return true;
- //request migh have been done
+ // Request migh have been done
} else {
- //did not request yet, do request
+ // Did not request yet, do request
Error err = client->request(method, request_string, headers, request_data);
if (err != OK) {
@@ -340,13 +327,13 @@ bool HTTPRequest::_update_connection() {
request_sent = true;
return false;
}
- } break; //connected: { } break requests only accepted here
+ } break; // Connected: break requests only accepted here
case HTTPClient::STATUS_REQUESTING: {
- //must wait, it's requesting
+ // Must wait, still requesting
client->poll();
return false;
- } break; // request in progress
+ } break; // Request in progress
case HTTPClient::STATUS_BODY: {
if (!got_response) {
@@ -363,7 +350,7 @@ bool HTTPRequest::_update_connection() {
}
if (client->is_response_chunked()) {
- body_len = -1; //no body len because chunked, change your webserver configuration if you want body len
+ body_len = -1; // No body len because chunked, change your webserver configuration if you want body len
} else {
body_len = client->get_response_body_length();
@@ -383,7 +370,6 @@ bool HTTPRequest::_update_connection() {
}
}
- //print_line("BODY: "+itos(body.size()));
client->poll();
PoolByteArray chunk = client->read_response_body_chunk();
@@ -411,15 +397,11 @@ bool HTTPRequest::_update_connection() {
call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
return true;
}
- /*if (body.size()>=body_len) {
- call_deferred("_request_done",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
- return true;
- }*/
}
return false;
- } break; // request resulted in body: { } break which must be read
+ } break; // Request resulted in body: break which must be read
case HTTPClient::STATUS_CONNECTION_ERROR: {
call_deferred("_request_done", RESULT_CONNECTION_ERROR, 0, PoolStringArray(), PoolByteArray());
return true;
@@ -449,7 +431,7 @@ void HTTPRequest::_notification(int p_what) {
if (done) {
set_process_internal(false);
- //cancel_request(); called from _request done now
+ // cancel_request(); called from _request done now
}
}
@@ -543,7 +525,7 @@ void HTTPRequest::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000"), "set_body_size_limit", "get_body_size_limit");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,1024"), "set_max_redirects", "get_max_redirects");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::POOL_STRING_ARRAY, "headers"), PropertyInfo(Variant::POOL_BYTE_ARRAY, "body")));
diff --git a/scene/main/http_request.h b/scene/main/http_request.h
index 790ff5f7ef..ab5a79c40d 100644
--- a/scene/main/http_request.h
+++ b/scene/main/http_request.h
@@ -42,7 +42,6 @@ class HTTPRequest : public Node {
public:
enum Result {
RESULT_SUCCESS,
- //RESULT_NO_BODY,
RESULT_CHUNKED_BODY_SIZE_MISMATCH,
RESULT_CANT_CONNECT,
RESULT_CANT_RESOLVE,
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cae368aeca..efc5d269a6 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -177,8 +177,8 @@ void Node::_propagate_ready() {
}
data.blocked--;
if (data.ready_first) {
- notification(NOTIFICATION_READY);
data.ready_first = false;
+ notification(NOTIFICATION_READY);
}
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 5ee286c2d5..deb40800bc 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -794,6 +794,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
Vector3(0, 0, 1)
};
+ /* clang-format off */
int diamond_faces[8 * 3] = {
0, 2, 4,
0, 3, 4,
@@ -804,6 +805,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
1, 2, 5,
1, 3, 5,
};
+ /* clang-format on */
PoolVector<int> indices;
for (int i = 0; i < 8 * 3; i++)
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 7898dc065a..244fc8da62 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -420,8 +420,8 @@ public:
void set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, const Size2 p_minsize, real_t p_shrink = 1);
-//void change_scene(const String& p_path);
-//Node *get_loaded_scene();
+ //void change_scene(const String& p_path);
+ //Node *get_loaded_scene();
#ifdef TOOLS_ENABLED
void set_edited_scene_root(Node *p_node);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index d864b0f763..1666aa5415 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -936,6 +936,9 @@ void Viewport::_camera_remove(Camera *p_camera) {
cameras.erase(p_camera);
if (camera == p_camera) {
+ if (camera && find_world().is_valid()) {
+ camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
+ }
camera = NULL;
}
}
@@ -1266,12 +1269,9 @@ Transform2D Viewport::_get_input_pre_xform() const {
Vector2 Viewport::_get_window_offset() const {
- /*
- if (parent_control) {
- return (parent_control->get_viewport()->get_final_transform() * parent_control->get_global_transform_with_canvas()).get_origin();
+ if (get_parent() && get_parent()->has_method("get_global_position")) {
+ return get_parent()->call("get_global_position");
}
- */
-
return Vector2();
}
@@ -1421,7 +1421,7 @@ void Viewport::_gui_show_tooltip() {
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);
+ gui.tooltip_label->set_text(tooltip.strip_edges());
Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_combined_minimum_size() + ttp->get_minimum_size());
Rect2 vr = gui.tooltip_label->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
@@ -1646,6 +1646,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
} else {
+ bool is_handled = false;
+
_gui_sort_modal_stack();
while (!gui.modal_stack.empty()) {
@@ -1663,11 +1665,20 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
top->notification(Control::NOTIFICATION_MODAL_CLOSE);
top->_modal_stack_remove();
top->hide();
+
+ if (!top->pass_on_modal_close_click()) {
+ is_handled = true;
+ }
} else {
break;
}
}
+ if (is_handled) {
+ get_tree()->set_input_as_handled();
+ return;
+ }
+
//Matrix32 parent_xform;
/*
@@ -1800,7 +1811,8 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
_gui_call_input(gui.mouse_focus, mb);
}
- if (mb->get_button_index() == gui.mouse_focus_button) {
+ if (mb->get_button_mask() == 0) {
+ // Last mouse button was released
gui.mouse_focus = NULL;
gui.mouse_focus_button = -1;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d6557f508e..9715e1d6a0 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -160,6 +160,7 @@
#include "scene/3d/area.h"
#include "scene/3d/arvr_nodes.h"
#include "scene/3d/audio_stream_player_3d.h"
+#include "scene/3d/baked_lightmap.h"
#include "scene/3d/bone_attachment.h"
#include "scene/3d/camera.h"
#include "scene/3d/collision_polygon.h"
@@ -375,6 +376,8 @@ void register_scene_types() {
ClassDB::register_class<ReflectionProbe>();
ClassDB::register_class<GIProbe>();
ClassDB::register_class<GIProbeData>();
+ ClassDB::register_class<BakedLightmap>();
+ ClassDB::register_class<BakedLightmapData>();
ClassDB::register_class<AnimationTreePlayer>();
ClassDB::register_class<Particles>();
ClassDB::register_class<Position3D>();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 8192074c17..4544549f94 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -77,6 +77,8 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
track_set_interpolation_loop_wrap(track, p_value);
else if (what == "imported")
track_set_imported(track, p_value);
+ else if (what == "enabled")
+ track_set_enabled(track, p_value);
else if (what == "keys" || what == "key_values") {
if (track_get_type(track) == TYPE_TRANSFORM) {
@@ -247,6 +249,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = track_get_interpolation_loop_wrap(track);
else if (what == "imported")
r_ret = track_is_imported(track);
+ else if (what == "enabled")
+ r_ret = track_is_enabled(track);
else if (what == "keys") {
if (track_get_type(track) == TYPE_TRANSFORM) {
@@ -391,6 +395,7 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
}
@@ -1575,6 +1580,19 @@ bool Animation::track_is_imported(int p_track) const {
return tracks[p_track]->imported;
}
+void Animation::track_set_enabled(int p_track, bool p_enabled) {
+
+ ERR_FAIL_INDEX(p_track, tracks.size());
+ tracks[p_track]->enabled = p_enabled;
+ emit_changed();
+}
+
+bool Animation::track_is_enabled(int p_track) const {
+
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
+ return tracks[p_track]->enabled;
+}
+
void Animation::track_move_down(int p_track) {
if (p_track > 0 && p_track < tracks.size()) {
@@ -1595,6 +1613,22 @@ float Animation::get_step() const {
return step;
}
+void Animation::copy_track(int src_track, Ref<Animation> p_to_animation) {
+ ERR_FAIL_COND(p_to_animation.is_null());
+ ERR_FAIL_INDEX(src_track, get_track_count());
+ int dst_track = p_to_animation->get_track_count();
+ p_to_animation->add_track(track_get_type(src_track));
+
+ p_to_animation->track_set_path(dst_track, track_get_path(src_track));
+ p_to_animation->track_set_imported(dst_track, track_is_imported(src_track));
+ p_to_animation->track_set_enabled(dst_track, track_is_enabled(src_track));
+ p_to_animation->track_set_interpolation_type(dst_track, track_get_interpolation_type(src_track));
+ p_to_animation->track_set_interpolation_loop_wrap(dst_track, track_get_interpolation_loop_wrap(src_track));
+ for (int i = 0; i < track_get_key_count(src_track); i++) {
+ p_to_animation->track_insert_key(dst_track, track_get_key_time(src_track, i), track_get_key_value(src_track, i), track_get_key_transition(src_track, i));
+ }
+}
+
void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_track", "type", "at_position"), &Animation::add_track, DEFVAL(-1));
@@ -1611,6 +1645,9 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_imported", "idx", "imported"), &Animation::track_set_imported);
ClassDB::bind_method(D_METHOD("track_is_imported", "idx"), &Animation::track_is_imported);
+ ClassDB::bind_method(D_METHOD("track_set_enabled", "idx", "enabled"), &Animation::track_set_enabled);
+ ClassDB::bind_method(D_METHOD("track_is_enabled", "idx"), &Animation::track_is_enabled);
+
ClassDB::bind_method(D_METHOD("transform_track_insert_key", "idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
ClassDB::bind_method(D_METHOD("track_insert_key", "idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "idx", "key_idx"), &Animation::track_remove_key);
@@ -1650,6 +1687,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
+ ClassDB::bind_method(D_METHOD("copy_track", "track", "to_animation"), &Animation::copy_track);
BIND_ENUM_CONSTANT(TYPE_VALUE);
BIND_ENUM_CONSTANT(TYPE_TRANSFORM);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 6235e161a3..1f468b29b5 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -67,10 +67,12 @@ private:
bool loop_wrap;
NodePath path; // path to something
bool imported;
+ bool enabled;
Track() {
interpolation = INTERPOLATION_LINEAR;
imported = false;
loop_wrap = true;
+ enabled = true;
}
virtual ~Track() {}
};
@@ -239,6 +241,9 @@ public:
void track_set_imported(int p_track, bool p_imported);
bool track_is_imported(int p_track) const;
+ void track_set_enabled(int p_track, bool p_enabled);
+ bool track_is_enabled(int p_track) const;
+
int transform_track_insert_key(int p_track, float p_time, const Vector3 p_loc, const Quat &p_rot = Quat(), const Vector3 &p_scale = Vector3());
void track_insert_key(int p_track, float p_time, const Variant &p_key, float p_transition = 1);
void track_set_key_transition(int p_track, int p_key_idx, float p_transition);
@@ -269,6 +274,8 @@ public:
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
+ void copy_track(int p_track, Ref<Animation> p_to_animation);
+
void set_length(float p_length);
float get_length() const;
diff --git a/scene/resources/box_shape.cpp b/scene/resources/box_shape.cpp
index 4b9843f3f5..858e19c9a6 100644
--- a/scene/resources/box_shape.cpp
+++ b/scene/resources/box_shape.cpp
@@ -73,8 +73,8 @@ void BoxShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
}
-BoxShape::BoxShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_BOX)) {
+BoxShape::BoxShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_BOX)) {
set_extents(Vector3(1, 1, 1));
}
diff --git a/scene/resources/capsule_shape.cpp b/scene/resources/capsule_shape.cpp
index e11b98f82e..f0eb8232e5 100644
--- a/scene/resources/capsule_shape.cpp
+++ b/scene/resources/capsule_shape.cpp
@@ -113,8 +113,8 @@ void CapsuleShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.01,4096,0.01"), "set_height", "get_height");
}
-CapsuleShape::CapsuleShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CAPSULE)) {
+CapsuleShape::CapsuleShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CAPSULE)) {
radius = 1.0;
height = 1.0;
diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp
index 912150b939..3caf12feb8 100644
--- a/scene/resources/capsule_shape_2d.cpp
+++ b/scene/resources/capsule_shape_2d.cpp
@@ -97,8 +97,8 @@ void CapsuleShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "height"), "set_height", "get_height");
}
-CapsuleShape2D::CapsuleShape2D()
- : Shape2D(Physics2DServer::get_singleton()->capsule_shape_create()) {
+CapsuleShape2D::CapsuleShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->capsule_shape_create()) {
radius = 10;
height = 20;
diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp
index 287bde4bfb..bff3ed4d67 100644
--- a/scene/resources/circle_shape_2d.cpp
+++ b/scene/resources/circle_shape_2d.cpp
@@ -76,8 +76,8 @@ void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) {
VisualServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col);
}
-CircleShape2D::CircleShape2D()
- : Shape2D(Physics2DServer::get_singleton()->circle_shape_create()) {
+CircleShape2D::CircleShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->circle_shape_create()) {
radius = 10;
_update_shape();
diff --git a/scene/resources/concave_polygon_shape.cpp b/scene/resources/concave_polygon_shape.cpp
index 6ae4fde85e..1c48f0c30b 100644
--- a/scene/resources/concave_polygon_shape.cpp
+++ b/scene/resources/concave_polygon_shape.cpp
@@ -106,8 +106,8 @@ void ConcavePolygonShape::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_faces"), &ConcavePolygonShape::get_faces);
}
-ConcavePolygonShape::ConcavePolygonShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONCAVE_POLYGON)) {
+ConcavePolygonShape::ConcavePolygonShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONCAVE_POLYGON)) {
//set_planes(Vector3(1,1,1));
}
diff --git a/scene/resources/concave_polygon_shape_2d.cpp b/scene/resources/concave_polygon_shape_2d.cpp
index bb91e33ec2..e90046fd28 100644
--- a/scene/resources/concave_polygon_shape_2d.cpp
+++ b/scene/resources/concave_polygon_shape_2d.cpp
@@ -84,6 +84,6 @@ void ConcavePolygonShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "segments"), "set_segments", "get_segments");
}
-ConcavePolygonShape2D::ConcavePolygonShape2D()
- : Shape2D(Physics2DServer::get_singleton()->concave_polygon_shape_create()) {
+ConcavePolygonShape2D::ConcavePolygonShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->concave_polygon_shape_create()) {
}
diff --git a/scene/resources/convex_polygon_shape.cpp b/scene/resources/convex_polygon_shape.cpp
index bba52bd5ff..31c4ea55e9 100644
--- a/scene/resources/convex_polygon_shape.cpp
+++ b/scene/resources/convex_polygon_shape.cpp
@@ -81,8 +81,8 @@ void ConvexPolygonShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "points"), "set_points", "get_points");
}
-ConvexPolygonShape::ConvexPolygonShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON)) {
+ConvexPolygonShape::ConvexPolygonShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON)) {
//set_points(Vector3(1,1,1));
}
diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp
index a76b6a7cf4..9c25b6b467 100644
--- a/scene/resources/convex_polygon_shape_2d.cpp
+++ b/scene/resources/convex_polygon_shape_2d.cpp
@@ -86,8 +86,8 @@ Rect2 ConvexPolygonShape2D::get_rect() const {
return rect;
}
-ConvexPolygonShape2D::ConvexPolygonShape2D()
- : Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) {
+ConvexPolygonShape2D::ConvexPolygonShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->convex_polygon_shape_create()) {
int pcount = 3;
for (int i = 0; i < pcount; i++)
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index c5feecd7c4..f4e6c5e247 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -257,6 +257,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// LinkButton
+ theme->set_stylebox("focus", "LinkButton", focus);
+
theme->set_font("font", "LinkButton", default_font);
theme->set_color("font_color", "LinkButton", control_font_color);
@@ -348,15 +350,15 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// CheckBox
Ref<StyleBox> cbx_empty = memnew(StyleBoxEmpty);
- cbx_empty->set_default_margin(MARGIN_LEFT, 22 * scale);
+ cbx_empty->set_default_margin(MARGIN_LEFT, 4 * scale);
cbx_empty->set_default_margin(MARGIN_RIGHT, 4 * scale);
cbx_empty->set_default_margin(MARGIN_TOP, 4 * scale);
- cbx_empty->set_default_margin(MARGIN_BOTTOM, 5 * scale);
+ cbx_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale);
Ref<StyleBox> cbx_focus = focus;
cbx_focus->set_default_margin(MARGIN_LEFT, 4 * scale);
- cbx_focus->set_default_margin(MARGIN_RIGHT, 22 * scale);
+ cbx_focus->set_default_margin(MARGIN_RIGHT, 4 * scale);
cbx_focus->set_default_margin(MARGIN_TOP, 4 * scale);
- cbx_focus->set_default_margin(MARGIN_BOTTOM, 5 * scale);
+ cbx_focus->set_default_margin(MARGIN_BOTTOM, 4 * scale);
theme->set_stylebox("normal", "CheckBox", cbx_empty);
theme->set_stylebox("pressed", "CheckBox", cbx_empty);
@@ -383,7 +385,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Ref<StyleBox> cb_empty = memnew(StyleBoxEmpty);
cb_empty->set_default_margin(MARGIN_LEFT, 6 * scale);
- cb_empty->set_default_margin(MARGIN_RIGHT, 70 * scale);
+ cb_empty->set_default_margin(MARGIN_RIGHT, 6 * scale);
cb_empty->set_default_margin(MARGIN_TOP, 4 * scale);
cb_empty->set_default_margin(MARGIN_BOTTOM, 4 * scale);
@@ -448,6 +450,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("normal", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3));
theme->set_stylebox("focus", "TextEdit", focus);
+ theme->set_stylebox("read_only", "TextEdit", make_stylebox(tree_bg_disabled_png, 4, 4, 4, 4));
theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3));
theme->set_icon("tab", "TextEdit", make_icon(tab_png));
@@ -465,6 +468,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("selection_color", "TextEdit", font_color_selection);
theme->set_color("mark_color", "TextEdit", Color(1.0, 0.4, 0.4, 0.4));
theme->set_color("breakpoint_color", "TextEdit", Color(0.8, 0.8, 0.4, 0.2));
+ theme->set_color("code_folding_color", "TextEdit", Color(0.8, 0.8, 0.8, 0.8));
theme->set_color("current_line_color", "TextEdit", Color(0.25, 0.25, 0.26, 0.8));
theme->set_color("caret_color", "TextEdit", control_font_color);
theme->set_color("caret_background_color", "TextEdit", Color::html("000000"));
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 255bb4c6dd..c6b37cad5a 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -410,6 +410,10 @@ static const unsigned char tree_bg_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x4, 0x3, 0x0, 0x0, 0x0, 0x7f, 0x1c, 0xd2, 0x8e, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2a, 0x50, 0x4c, 0x54, 0x45, 0x17, 0x16, 0x1a, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x21, 0x1f, 0x25, 0x1d, 0x1c, 0x21, 0x20, 0x1e, 0x24, 0x1d, 0x1c, 0x21, 0x1d, 0x1c, 0x21, 0x24, 0x22, 0x29, 0x28, 0x26, 0x2d, 0x28, 0x26, 0x2e, 0x2b, 0x2a, 0x31, 0x2c, 0x2a, 0x32, 0xff, 0xff, 0xff, 0xb9, 0x11, 0x56, 0x3e, 0x0, 0x0, 0x0, 0x8, 0x74, 0x52, 0x4e, 0x53, 0x6f, 0xef, 0xf7, 0xf7, 0xf0, 0xf9, 0xf1, 0xee, 0xcf, 0x21, 0xd2, 0xdf, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0xd, 0xf6, 0xb4, 0x61, 0xf5, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x63, 0x60, 0x54, 0x36, 0x36, 0x12, 0x60, 0xf0, 0x98, 0xb5, 0x6a, 0x65, 0xb, 0x43, 0xe4, 0x9e, 0x33, 0xa7, 0xa7, 0x32, 0x58, 0x9d, 0x39, 0x73, 0x66, 0x31, 0x16, 0x12, 0x22, 0xb, 0x52, 0xd9, 0xc6, 0xc0, 0x2, 0xd4, 0x55, 0x0, 0x0, 0xc, 0x14, 0x1a, 0x90, 0x55, 0x1a, 0xec, 0xdb, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char tree_bg_disabled_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0xa, 0x8, 0x4, 0x0, 0x0, 0x0, 0x27, 0x3b, 0x7, 0x36, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x87, 0x8f, 0xcc, 0xbf, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xe0, 0x6, 0x16, 0x12, 0x2b, 0x5, 0x39, 0x1a, 0x32, 0x39, 0x0, 0x0, 0x0, 0x64, 0x49, 0x44, 0x41, 0x54, 0x8, 0xd7, 0x95, 0xce, 0x31, 0x12, 0x2, 0x21, 0x10, 0x44, 0xd1, 0x3f, 0x40, 0xa1, 0x44, 0xa6, 0x46, 0xde, 0x63, 0x4f, 0xe5, 0x15, 0x38, 0xb2, 0xd6, 0x6, 0xb0, 0xc8, 0x30, 0x6, 0x96, 0xac, 0x56, 0x99, 0xf8, 0xb3, 0x7e, 0x51, 0xcb, 0xf9, 0x1a, 0xb3, 0x3f, 0xa, 0xaf, 0xc, 0xad, 0x2d, 0xcb, 0xe5, 0x76, 0x38, 0x5, 0x76, 0xec, 0x6c, 0xf7, 0xe0, 0x53, 0xe0, 0x13, 0xa1, 0x27, 0x27, 0x43, 0x26, 0x81, 0x20, 0xc8, 0x70, 0xfc, 0xe8, 0xf, 0x34, 0x67, 0xd8, 0x9c, 0x86, 0x61, 0x2e, 0x68, 0xe9, 0x91, 0xaf, 0x4b, 0x5a, 0x7d, 0x2a, 0x2c, 0x3, 0xed, 0xef, 0x1e, 0x6b, 0xcb, 0x4f, 0xa6, 0x66, 0x2b, 0x25, 0x6, 0x1, 0x37, 0x40, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char tree_bg_focus_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x20, 0x8, 0x6, 0x0, 0x0, 0x0, 0x73, 0x7a, 0x7a, 0xf4, 0x0, 0x0, 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xb1, 0x8f, 0xb, 0xfc, 0x61, 0x5, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, 0x7a, 0x26, 0x0, 0x0, 0x80, 0x84, 0x0, 0x0, 0xfa, 0x0, 0x0, 0x0, 0x80, 0xe8, 0x0, 0x0, 0x75, 0x30, 0x0, 0x0, 0xea, 0x60, 0x0, 0x0, 0x3a, 0x98, 0x0, 0x0, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, 0x45, 0x7, 0xdb, 0xb, 0x4, 0x12, 0x2d, 0x3a, 0xb5, 0x1b, 0x14, 0x49, 0x0, 0x0, 0x2, 0xd9, 0x49, 0x44, 0x41, 0x54, 0x58, 0xc3, 0xc5, 0x57, 0x31, 0x8e, 0x14, 0x31, 0x10, 0xac, 0xea, 0x3d, 0x11, 0x91, 0xdf, 0x49, 0xf7, 0xf, 0x78, 0x1, 0xf, 0x40, 0x84, 0x44, 0x10, 0xf2, 0x20, 0x42, 0x62, 0x12, 0xc4, 0x3, 0x78, 0x1, 0xff, 0x21, 0x42, 0xb8, 0x8a, 0xc0, 0x1e, 0x4f, 0xdb, 0xe3, 0xb9, 0xbb, 0x45, 0x20, 0x7c, 0x1a, 0xdd, 0x8c, 0xec, 0xed, 0xae, 0xae, 0xea, 0xee, 0xe9, 0xe1, 0xfd, 0xed, 0xdd, 0xb, 0x0, 0xdf, 0xf1, 0x7f, 0xd6, 0x4b, 0xde, 0xdf, 0xde, 0xf9, 0xcb, 0xeb, 0x9f, 0xff, 0xc5, 0xfb, 0x9b, 0xaf, 0xcf, 0x70, 0xb3, 0x3d, 0x3c, 0xff, 0xf0, 0xd, 0x24, 0x11, 0x71, 0x1, 0x49, 0x90, 0x4, 0x0, 0x4, 0xd3, 0x2f, 0xb6, 0x7b, 0xf, 0xff, 0x86, 0x2d, 0x8c, 0x47, 0x60, 0x1b, 0x0, 0x61, 0x1b, 0xb6, 0x21, 0x15, 0xd8, 0xc6, 0x8f, 0x8f, 0xaf, 0x0, 0x60, 0x7, 0x0, 0xa2, 0x2, 0x20, 0x11, 0x41, 0x30, 0xa2, 0x2, 0xc1, 0xb5, 0x0, 0x8c, 0x79, 0xd9, 0x80, 0x24, 0x48, 0x0, 0x48, 0x28, 0x9d, 0xb9, 0xd9, 0xd, 0xb0, 0x31, 0x40, 0x5c, 0x2e, 0x1, 0x46, 0x0, 0xd, 0xd0, 0xb6, 0xef, 0x35, 0x8e, 0x13, 0x6, 0xbc, 0x33, 0x60, 0xa3, 0x9a, 0x11, 0x6c, 0x82, 0xe6, 0xa, 0x0, 0x86, 0xe8, 0x2f, 0x71, 0xa9, 0xf7, 0x9c, 0x4d, 0x1f, 0x69, 0x5e, 0x1, 0xd8, 0xa3, 0x37, 0xa4, 0xa, 0x27, 0x1a, 0x13, 0x79, 0x25, 0x9, 0xaa, 0xe3, 0xd8, 0x9c, 0x5f, 0xea, 0x7d, 0x67, 0x80, 0xd7, 0x31, 0x20, 0xab, 0x3, 0x20, 0x5, 0x20, 0x2a, 0x13, 0x41, 0x40, 0x2b, 0x0, 0x76, 0x22, 0xb9, 0xb1, 0xd1, 0x92, 0x91, 0x5d, 0x86, 0xf3, 0x35, 0xef, 0x5, 0xa3, 0x25, 0x60, 0xd, 0x6e, 0x66, 0xe5, 0x0, 0xc0, 0xa8, 0x54, 0x1, 0x6, 0xe9, 0x16, 0x9f, 0x4f, 0xdc, 0x1c, 0x13, 0xed, 0xb1, 0x55, 0x31, 0x78, 0x70, 0x8e, 0xca, 0xcb, 0x8e, 0x6a, 0xbe, 0x56, 0x88, 0x67, 0x38, 0xdb, 0x75, 0x58, 0x76, 0xdf, 0x1b, 0x6c, 0x7a, 0xc, 0x6c, 0x90, 0x0, 0xfd, 0xe0, 0xe8, 0x98, 0xe4, 0x4, 0xe2, 0x69, 0xc, 0xe4, 0x40, 0xaa, 0xf9, 0x63, 0x70, 0xbb, 0x4, 0x16, 0x9c, 0x12, 0xa7, 0x3e, 0xe7, 0x38, 0xb3, 0x4, 0x2b, 0x6f, 0xc7, 0x23, 0xbb, 0xc3, 0x66, 0xdb, 0x38, 0x7, 0x0, 0xcf, 0x32, 0x34, 0xb6, 0x52, 0xb4, 0x3c, 0xf7, 0x35, 0x10, 0xd9, 0xf7, 0x6, 0x56, 0x93, 0x4, 0x4b, 0x0, 0x43, 0x28, 0xf9, 0x4a, 0x3d, 0x6e, 0xb0, 0x3e, 0x9, 0x31, 0xa9, 0x62, 0xd6, 0xc4, 0x36, 0x9c, 0xf2, 0xd9, 0xbd, 0x41, 0x2d, 0x24, 0x68, 0xc7, 0x1f, 0x62, 0x60, 0x6a, 0xc1, 0x7f, 0x95, 0x81, 0xaa, 0x13, 0x61, 0x9, 0xa6, 0xf6, 0x1c, 0x30, 0x17, 0x11, 0x6f, 0x48, 0x12, 0x3b, 0xf3, 0x7b, 0xc2, 0xeb, 0xca, 0x92, 0xb7, 0x72, 0x5f, 0x31, 0x90, 0x11, 0x4e, 0x48, 0xb3, 0x13, 0xa6, 0xcc, 0x3e, 0x51, 0x60, 0x91, 0x53, 0x55, 0x87, 0xf3, 0x2a, 0x98, 0xff, 0x7c, 0x6c, 0x1a, 0xf9, 0xec, 0xda, 0xeb, 0x94, 0x13, 0x43, 0xdd, 0x3f, 0x2a, 0x41, 0xd2, 0xb, 0x27, 0x65, 0xe8, 0x13, 0x20, 0x29, 0x3f, 0xc6, 0x7c, 0x48, 0x65, 0x8, 0x41, 0xda, 0xa4, 0xd5, 0x11, 0xc0, 0x2a, 0x61, 0xce, 0x18, 0x58, 0xd2, 0xbe, 0x98, 0x48, 0x96, 0xdd, 0xf5, 0xbc, 0x11, 0x65, 0xb6, 0x12, 0x55, 0xcb, 0xde, 0xb3, 0x78, 0x27, 0x78, 0xdc, 0xcb, 0x72, 0xe6, 0xd7, 0x8a, 0x27, 0xe6, 0x52, 0x1f, 0x10, 0xe0, 0xb8, 0x92, 0x81, 0xb4, 0x3f, 0x1, 0x1d, 0xed, 0x6c, 0xd4, 0xef, 0x12, 0x2f, 0x73, 0xa0, 0xe, 0xf, 0x42, 0x24, 0x20, 0xf, 0x91, 0xbf, 0x6e, 0x44, 0xfb, 0xde, 0xa1, 0x4, 0xa5, 0x7, 0xaa, 0xa0, 0x23, 0xad, 0xd4, 0x4b, 0xaa, 0x73, 0x0, 0xb7, 0xa1, 0x82, 0x87, 0x46, 0x74, 0x9a, 0xf, 0x48, 0x5d, 0xb3, 0xd, 0xa2, 0xb0, 0x96, 0xe5, 0x7d, 0xc8, 0x81, 0x19, 0xf1, 0x36, 0x2b, 0xba, 0xbd, 0x5e, 0xb3, 0xb7, 0x55, 0x12, 0x76, 0x90, 0xc4, 0x3a, 0x1, 0xa7, 0x66, 0xbc, 0x3, 0x90, 0xe0, 0x8, 0xc8, 0x42, 0x29, 0xa5, 0x7b, 0xa2, 0x63, 0x9f, 0x88, 0x26, 0xa, 0x9c, 0x85, 0x6f, 0x7b, 0x2b, 0x0, 0x92, 0x51, 0x4a, 0x81, 0x5c, 0x2a, 0xcb, 0x2a, 0x47, 0x0, 0xb0, 0x2b, 0x8, 0x11, 0x62, 0x20, 0x58, 0x20, 0x47, 0x77, 0x5a, 0xe5, 0x98, 0x43, 0x3f, 0x2, 0xd8, 0xc1, 0xa5, 0x39, 0x40, 0x82, 0xb5, 0xd9, 0xd7, 0x5a, 0x2, 0x59, 0x80, 0x9, 0x8a, 0x0, 0x7f, 0x1, 0x8, 0x44, 0x8, 0x64, 0xe0, 0xe8, 0x6e, 0x4b, 0xb4, 0x87, 0xa6, 0xc4, 0x3d, 0x17, 0x24, 0xa3, 0xa8, 0xc0, 0x32, 0x8a, 0xea, 0x33, 0x67, 0x0, 0x6e, 0xc, 0x94, 0x5e, 0xe2, 0x46, 0x11, 0x3a, 0x0, 0x82, 0x57, 0x1, 0x98, 0xbf, 0xb, 0xa4, 0xf6, 0x55, 0xd4, 0x2a, 0xe1, 0x0, 0x40, 0xf5, 0xf3, 0xa5, 0xd3, 0x16, 0x6a, 0xb4, 0xa7, 0x19, 0xfa, 0x4f, 0x18, 0xe8, 0x2c, 0x34, 0xfb, 0x92, 0x20, 0xbb, 0xf, 0xa3, 0x1d, 0xc0, 0xcd, 0xe7, 0xb7, 0x83, 0xf9, 0xf2, 0x24, 0xd3, 0xd7, 0xaf, 0x40, 0x9a, 0x84, 0x1, 0xf0, 0xfe, 0xf6, 0xee, 0x1d, 0x80, 0x4f, 0xff, 0xc8, 0xdf, 0x63, 0xeb, 0xfd, 0x6f, 0x3, 0x74, 0x35, 0xa7, 0x2a, 0xf0, 0x17, 0xed, 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, 0x17, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xc9, 0xad, 0xc8, 0x52, 0x0, 0x0, 0x0, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x36, 0x2d, 0x32, 0x32, 0x54, 0x32, 0x30, 0x3a, 0x33, 0x39, 0x3a, 0x32, 0x36, 0x2b, 0x30, 0x32, 0x3a, 0x30, 0x30, 0xb8, 0xf0, 0x70, 0xee, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/default_theme/tree_bg_disabled.png b/scene/resources/default_theme/tree_bg_disabled.png
new file mode 100644
index 0000000000..a0fa505e4c
--- /dev/null
+++ b/scene/resources/default_theme/tree_bg_disabled.png
Binary files differ
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 15710f4c14..326320c60f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -215,6 +215,13 @@ bool ShaderMaterial::_can_do_next_pass() const {
return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL;
}
+Shader::Mode ShaderMaterial::get_shader_mode() const {
+ if (shader.is_valid())
+ return shader->get_mode();
+ else
+ return Shader::MODE_SPATIAL;
+}
+
ShaderMaterial::ShaderMaterial() {
}
@@ -638,7 +645,7 @@ void SpatialMaterial::_update_shader() {
code += "\tvec2 base_uv = UV;\n";
}
- if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2])) {
+ if ((features[FEATURE_DETAIL] && detail_uv == DETAIL_UV_2) || (features[FEATURE_AMBIENT_OCCLUSION] && flags[FLAG_AO_ON_UV2]) || (features[FEATURE_EMISSION] && flags[FLAG_EMISSION_ON_UV2])) {
code += "\tvec2 base_uv2 = UV2;\n";
}
@@ -722,11 +729,20 @@ void SpatialMaterial::_update_shader() {
}
if (features[FEATURE_EMISSION]) {
- if (flags[FLAG_UV1_USE_TRIPLANAR]) {
- code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ if (flags[FLAG_EMISSION_ON_UV2]) {
+ if (flags[FLAG_UV2_USE_TRIPLANAR]) {
+ code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv2_power_normal,uv2_triplanar_pos).rgb;\n";
+ } else {
+ code += "\tvec3 emission_tex = texture(texture_emission,base_uv2).rgb;\n";
+ }
} else {
- code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
+ if (flags[FLAG_UV1_USE_TRIPLANAR]) {
+ code += "\tvec3 emission_tex = triplanar_texture(texture_emission,uv1_power_normal,uv1_triplanar_pos).rgb;\n";
+ } else {
+ code += "\tvec3 emission_tex = texture(texture_emission,base_uv).rgb;\n";
+ }
}
+
if (emission_op == EMISSION_OP_ADD) {
code += "\tEMISSION = (emission.rgb+emission_tex)*emission_energy;\n";
} else {
@@ -1662,6 +1678,11 @@ RID SpatialMaterial::get_shader_rid() const {
return shader_map[current_key].shader;
}
+Shader::Mode SpatialMaterial::get_shader_mode() const {
+
+ return Shader::MODE_SPATIAL;
+}
+
void SpatialMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &SpatialMaterial::set_albedo);
@@ -1880,6 +1901,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_emission_energy", "get_emission_energy");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator");
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_EMISSION);
ADD_GROUP("NormalMap", "normal_");
@@ -2022,6 +2044,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_UV1_USE_TRIPLANAR);
BIND_ENUM_CONSTANT(FLAG_UV2_USE_TRIPLANAR);
BIND_ENUM_CONSTANT(FLAG_AO_ON_UV2);
+ BIND_ENUM_CONSTANT(FLAG_EMISSION_ON_UV2);
BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR);
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
@@ -2054,8 +2077,8 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(EMISSION_OP_MULTIPLY);
}
-SpatialMaterial::SpatialMaterial()
- : element(this) {
+SpatialMaterial::SpatialMaterial() :
+ element(this) {
//initialize to right values
set_albedo(Color(1.0, 1.0, 1.0, 1.0));
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 374ec853dc..d5c3ef83e2 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -69,6 +69,8 @@ public:
int get_render_priority() const;
virtual RID get_rid() const;
+
+ virtual Shader::Mode get_shader_mode() const = 0;
Material();
virtual ~Material();
};
@@ -96,6 +98,8 @@ public:
void set_shader_param(const StringName &p_param, const Variant &p_value);
Variant get_shader_param(const StringName &p_param) const;
+ virtual Shader::Mode get_shader_mode() const;
+
ShaderMaterial();
~ShaderMaterial();
};
@@ -180,6 +184,7 @@ public:
FLAG_UV2_USE_TRIPLANAR,
FLAG_TRIPLANAR_USE_WORLD,
FLAG_AO_ON_UV2,
+ FLAG_EMISSION_ON_UV2,
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
FLAG_MAX
@@ -230,7 +235,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 13;
+ uint64_t flags : 14;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
@@ -600,6 +605,8 @@ public:
RID get_shader_rid() const;
+ virtual Shader::Mode get_shader_mode() const;
+
SpatialMaterial();
virtual ~SpatialMaterial();
};
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 0a886c25b1..bb33962be6 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -28,10 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "mesh.h"
+
+#include "pair.h"
#include "scene/resources/concave_polygon_shape.h"
#include "scene/resources/convex_polygon_shape.h"
#include "surface_tool.h"
+#include <stdlib.h>
+
void Mesh::_clear_triangle_mesh() const {
triangle_mesh.unref();
@@ -413,8 +417,21 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
return newmesh;
}
+void Mesh::set_lightmap_size_hint(const Vector2 &p_size) {
+ lightmap_size_hint = p_size;
+}
+
+Size2 Mesh::get_lightmap_size_hint() const {
+ return lightmap_size_hint;
+}
+
void Mesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &Mesh::set_lightmap_size_hint);
+ ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &Mesh::get_lightmap_size_hint);
+
+ ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "lightmap_size_hint"), "set_lightmap_size_hint", "get_lightmap_size_hint");
+
BIND_ENUM_CONSTANT(PRIMITIVE_POINTS);
BIND_ENUM_CONSTANT(PRIMITIVE_LINES);
BIND_ENUM_CONSTANT(PRIMITIVE_LINE_STRIP);
@@ -1035,6 +1052,202 @@ void ArrayMesh::regen_normalmaps() {
}
}
+//dirty hack
+bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, const int *p_face_materials, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) = NULL;
+
+struct ArrayMeshLightmapSurface {
+
+ Ref<Material> material;
+ Vector<SurfaceTool::Vertex> vertices;
+ Mesh::PrimitiveType primitive;
+ uint32_t format;
+};
+
+Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texel_size) {
+
+ ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
+ ERR_EXPLAIN("Can't unwrap mesh with blend shapes");
+ ERR_FAIL_COND_V(blend_shapes.size() != 0, ERR_UNAVAILABLE);
+
+ Vector<float> vertices;
+ Vector<float> normals;
+ Vector<int> indices;
+ Vector<int> face_materials;
+ Vector<float> uv;
+ Vector<Pair<int, int> > uv_index;
+
+ Vector<ArrayMeshLightmapSurface> surfaces;
+ for (int i = 0; i < get_surface_count(); i++) {
+ ArrayMeshLightmapSurface s;
+ s.primitive = surface_get_primitive_type(i);
+
+ if (s.primitive != Mesh::PRIMITIVE_TRIANGLES) {
+ ERR_EXPLAIN("Only triangles are supported for lightmap unwrap");
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+ s.format = surface_get_format(i);
+ if (!(s.format & ARRAY_FORMAT_NORMAL)) {
+ ERR_EXPLAIN("Normals are required for lightmap unwrap");
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+
+ Array arrays = surface_get_arrays(i);
+ s.material = surface_get_material(i);
+ s.vertices = SurfaceTool::create_vertex_array_from_triangle_arrays(arrays);
+
+ PoolVector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
+ int vc = rvertices.size();
+ PoolVector<Vector3>::Read r = rvertices.read();
+
+ PoolVector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
+ PoolVector<Vector3>::Read rn = rnormals.read();
+
+ int vertex_ofs = vertices.size() / 3;
+
+ vertices.resize((vertex_ofs + vc) * 3);
+ normals.resize((vertex_ofs + vc) * 3);
+ uv_index.resize(vertex_ofs + vc);
+
+ for (int j = 0; j < vc; j++) {
+
+ Vector3 v = p_base_transform.xform(r[j]);
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = rn[j].x;
+ normals[(j + vertex_ofs) * 3 + 1] = rn[j].y;
+ normals[(j + vertex_ofs) * 3 + 2] = rn[j].z;
+ uv_index[j + vertex_ofs] = Pair<int, int>(i, j);
+ }
+
+ PoolVector<int> rindices = arrays[Mesh::ARRAY_INDEX];
+ int ic = rindices.size();
+
+ if (ic == 0) {
+
+ for (int j = 0; j < vc / 3; j++) {
+ if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate())
+ continue;
+
+ indices.push_back(vertex_ofs + j * 3 + 0);
+ indices.push_back(vertex_ofs + j * 3 + 1);
+ indices.push_back(vertex_ofs + j * 3 + 2);
+ face_materials.push_back(i);
+ }
+
+ } else {
+ PoolVector<int>::Read ri = rindices.read();
+
+ for (int j = 0; j < ic / 3; j++) {
+ if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate())
+ continue;
+ indices.push_back(vertex_ofs + ri[j * 3 + 0]);
+ indices.push_back(vertex_ofs + ri[j * 3 + 1]);
+ indices.push_back(vertex_ofs + ri[j * 3 + 2]);
+ face_materials.push_back(i);
+ }
+ }
+
+ surfaces.push_back(s);
+ }
+
+ //unwrap
+
+ float *gen_uvs;
+ int *gen_vertices;
+ int *gen_indices;
+ int gen_vertex_count;
+ int gen_index_count;
+ int size_x;
+ int size_y;
+
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), face_materials.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
+
+ if (!ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ //remove surfaces
+ while (get_surface_count()) {
+ surface_remove(0);
+ }
+
+ //create surfacetools for each surface..
+ Vector<Ref<SurfaceTool> > surfaces_tools;
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ Ref<SurfaceTool> st;
+ st.instance();
+ st->begin(Mesh::PRIMITIVE_TRIANGLES);
+ st->set_material(surfaces[i].material);
+ surfaces_tools.push_back(st); //stay there
+ }
+
+ print_line("gen indices: " + itos(gen_index_count));
+ //go through all indices
+ for (int i = 0; i < gen_index_count; i += 3) {
+
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_index.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_index.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_index.size(), ERR_BUG);
+
+ ERR_FAIL_COND_V(uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 1]]].first || uv_index[gen_vertices[gen_indices[i + 0]]].first != uv_index[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
+
+ int surface = uv_index[gen_vertices[gen_indices[i + 0]]].first;
+
+ for (int j = 0; j < 3; j++) {
+
+ int vertex_idx = gen_vertices[gen_indices[i + j]];
+
+ SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second];
+
+ if (surfaces[surface].format & ARRAY_FORMAT_COLOR) {
+ surfaces_tools[surface]->add_color(v.color);
+ }
+ if (surfaces[surface].format & ARRAY_FORMAT_TEX_UV) {
+ surfaces_tools[surface]->add_uv(v.uv);
+ }
+ if (surfaces[surface].format & ARRAY_FORMAT_NORMAL) {
+ surfaces_tools[surface]->add_normal(v.normal);
+ }
+ if (surfaces[surface].format & ARRAY_FORMAT_TANGENT) {
+ Plane t;
+ t.normal = v.tangent;
+ t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
+ surfaces_tools[surface]->add_tangent(t);
+ }
+ if (surfaces[surface].format & ARRAY_FORMAT_BONES) {
+ surfaces_tools[surface]->add_bones(v.bones);
+ }
+ if (surfaces[surface].format & ARRAY_FORMAT_WEIGHTS) {
+ surfaces_tools[surface]->add_weights(v.weights);
+ }
+
+ Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
+ surfaces_tools[surface]->add_uv2(uv2);
+
+ surfaces_tools[surface]->add_vertex(v.vertex);
+ }
+ }
+
+ //free stuff
+ ::free(gen_vertices);
+ ::free(gen_indices);
+ ::free(gen_uvs);
+
+ //generate surfaces
+
+ for (int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ surfaces_tools[i]->commit(Ref<ArrayMesh>((ArrayMesh *)this), surfaces[i].format);
+ }
+
+ set_lightmap_size_hint(Size2(size_x, size_y));
+
+ return OK;
+}
+
void ArrayMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ArrayMesh::add_blend_shape);
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index b85a6a84af..ea38ebf2ff 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -43,6 +43,8 @@ class Mesh : public Resource {
GDCLASS(Mesh, Resource);
mutable Ref<TriangleMesh> triangle_mesh; //cached
+ Size2 lightmap_size_hint;
+
protected:
void _clear_triangle_mesh() const;
@@ -138,6 +140,9 @@ public:
virtual AABB get_aabb() const = 0;
+ void set_lightmap_size_hint(const Vector2 &p_size);
+ Size2 get_lightmap_size_hint() const;
+
Mesh();
};
@@ -216,6 +221,8 @@ public:
void center_geometry();
void regen_normalmaps();
+ Error lightmap_unwrap(const Transform &p_base_transform = Transform(), float p_texel_size = 0.05);
+
virtual void reload_from_file();
ArrayMesh();
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 06829089be..3a5cb7da2e 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -177,7 +177,7 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
node = Object::cast_to<Node>(obj);
} else {
- print_line("wtf class is disabled for: " + itos(n.type));
+ print_line("Class is disabled for: " + itos(n.type));
print_line("name: " + String(snames[n.type]));
}
diff --git a/scene/resources/plane_shape.cpp b/scene/resources/plane_shape.cpp
index 2b1e890a5d..2eb2ceff24 100644
--- a/scene/resources/plane_shape.cpp
+++ b/scene/resources/plane_shape.cpp
@@ -86,8 +86,8 @@ void PlaneShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PLANE, "plane"), "set_plane", "get_plane");
}
-PlaneShape::PlaneShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_PLANE)) {
+PlaneShape::PlaneShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_PLANE)) {
set_plane(Plane(0, 1, 0, 0));
}
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 3b80db291c..2e8f9cbb33 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -164,7 +164,7 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
diff --git a/scene/resources/ray_shape.cpp b/scene/resources/ray_shape.cpp
index ccce7660c6..b03a81b6bd 100644
--- a/scene/resources/ray_shape.cpp
+++ b/scene/resources/ray_shape.cpp
@@ -66,8 +66,8 @@ void RayShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "length", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_length", "get_length");
}
-RayShape::RayShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) {
+RayShape::RayShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_RAY)) {
set_length(1.0);
}
diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp
index 69dbb76744..202024aa96 100644
--- a/scene/resources/rectangle_shape_2d.cpp
+++ b/scene/resources/rectangle_shape_2d.cpp
@@ -66,8 +66,8 @@ void RectangleShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents");
}
-RectangleShape2D::RectangleShape2D()
- : Shape2D(Physics2DServer::get_singleton()->rectangle_shape_create()) {
+RectangleShape2D::RectangleShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->rectangle_shape_create()) {
extents = Vector2(10, 10);
_update_shape();
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index fe23fbf6b3..aebbb5b562 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scene_format_text.h"
-
+#include "core/io/resource_format_binary.h"
#include "os/dir_access.h"
#include "project_settings.h"
#include "version.h"
@@ -53,6 +53,60 @@ Ref<Resource> ResourceInteractiveLoaderText::get_resource() {
return resource;
}
+Error ResourceInteractiveLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
+
+ VariantParser::Token token;
+ VariantParser::get_token(p_stream, token, line, r_err_str);
+ if (token.type != VariantParser::TK_NUMBER) {
+ r_err_str = "Expected number (sub-resource index)";
+ return ERR_PARSE_ERROR;
+ }
+
+ int index = token.value;
+
+ if (!p_data->resource_map.has(index)) {
+ Ref<DummyResource> dr;
+ dr.instance();
+ dr->set_subindex(index);
+ p_data->resource_map[index] = dr;
+ p_data->resource_set.insert(dr);
+ }
+
+ r_res = p_data->resource_map[index];
+
+ VariantParser::get_token(p_stream, token, line, r_err_str);
+ if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
+Error ResourceInteractiveLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
+
+ VariantParser::Token token;
+ VariantParser::get_token(p_stream, token, line, r_err_str);
+ if (token.type != VariantParser::TK_NUMBER) {
+ r_err_str = "Expected number (sub-resource index)";
+ return ERR_PARSE_ERROR;
+ }
+
+ int id = token.value;
+
+ ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
+
+ r_res = p_data->rev_external_resources[id];
+
+ VariantParser::get_token(p_stream, token, line, r_err_str);
+ if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
Error ResourceInteractiveLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
VariantParser::Token token;
@@ -131,6 +185,203 @@ Error ResourceInteractiveLoaderText::_parse_ext_resource(VariantParser::Stream *
return OK;
}
+Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) {
+ Ref<PackedScene> packed_scene;
+ packed_scene.instance();
+
+ while (true) {
+
+ if (next_tag.name == "node") {
+
+ int parent = -1;
+ int owner = -1;
+ int type = -1;
+ int name = -1;
+ int instance = -1;
+ //int base_scene=-1;
+
+ if (next_tag.fields.has("name")) {
+ name = packed_scene->get_state()->add_name(next_tag.fields["name"]);
+ }
+
+ if (next_tag.fields.has("parent")) {
+ NodePath np = next_tag.fields["parent"];
+ np.prepend_period(); //compatible to how it manages paths internally
+ parent = packed_scene->get_state()->add_node_path(np);
+ }
+
+ if (next_tag.fields.has("type")) {
+ type = packed_scene->get_state()->add_name(next_tag.fields["type"]);
+ } else {
+ type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced
+ }
+
+ if (next_tag.fields.has("instance")) {
+
+ instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]);
+
+ if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) {
+ packed_scene->get_state()->set_base_scene(instance);
+ instance = -1;
+ }
+ }
+
+ if (next_tag.fields.has("instance_placeholder")) {
+
+ String path = next_tag.fields["instance_placeholder"];
+
+ int path_v = packed_scene->get_state()->add_value(path);
+
+ if (packed_scene->get_state()->get_node_count() == 0) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Instance Placeholder can't be used for inheritance.";
+ _printerr();
+ return Ref<PackedScene>();
+ }
+
+ instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER;
+ }
+
+ if (next_tag.fields.has("owner")) {
+ owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]);
+ } else {
+ if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1))
+ owner = 0; //if no owner, owner is root
+ }
+
+ int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance);
+
+ if (next_tag.fields.has("groups")) {
+
+ Array groups = next_tag.fields["groups"];
+ for (int i = 0; i < groups.size(); i++) {
+ packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
+ }
+ }
+
+ while (true) {
+
+ String assign;
+ Variant value;
+
+ error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser);
+
+ if (error) {
+ if (error != ERR_FILE_EOF) {
+ _printerr();
+ return Ref<PackedScene>();
+ } else {
+ return packed_scene;
+ }
+ }
+
+ if (assign != String()) {
+ int nameidx = packed_scene->get_state()->add_name(assign);
+ int valueidx = packed_scene->get_state()->add_value(value);
+ packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx);
+ //it's assignment
+ } else if (next_tag.name != String()) {
+ break;
+ }
+ }
+ } else if (next_tag.name == "connection") {
+
+ if (!next_tag.fields.has("from")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "missing 'from' field fron connection tag";
+ return Ref<PackedScene>();
+ }
+
+ if (!next_tag.fields.has("to")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "missing 'to' field fron connection tag";
+ return Ref<PackedScene>();
+ }
+
+ if (!next_tag.fields.has("signal")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "missing 'signal' field fron connection tag";
+ return Ref<PackedScene>();
+ }
+
+ if (!next_tag.fields.has("method")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "missing 'method' field fron connection tag";
+ return Ref<PackedScene>();
+ }
+
+ NodePath from = next_tag.fields["from"];
+ NodePath to = next_tag.fields["to"];
+ StringName method = next_tag.fields["method"];
+ StringName signal = next_tag.fields["signal"];
+ int flags = CONNECT_PERSIST;
+ Array binds;
+
+ if (next_tag.fields.has("flags")) {
+ flags = next_tag.fields["flags"];
+ }
+
+ if (next_tag.fields.has("binds")) {
+ binds = next_tag.fields["binds"];
+ }
+
+ Vector<int> bind_ints;
+ for (int i = 0; i < binds.size(); i++) {
+ bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
+ }
+
+ packed_scene->get_state()->add_connection(
+ packed_scene->get_state()->add_node_path(from.simplified()),
+ packed_scene->get_state()->add_node_path(to.simplified()),
+ packed_scene->get_state()->add_name(signal),
+ packed_scene->get_state()->add_name(method),
+ flags,
+ bind_ints);
+
+ error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser);
+
+ if (error) {
+ if (error != ERR_FILE_EOF) {
+ _printerr();
+ return Ref<PackedScene>();
+ } else {
+ return packed_scene;
+ }
+ }
+ } else if (next_tag.name == "editable") {
+
+ if (!next_tag.fields.has("path")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "missing 'path' field fron connection tag";
+ _printerr();
+ return Ref<PackedScene>();
+ }
+
+ NodePath path = next_tag.fields["path"];
+
+ packed_scene->get_state()->add_editable_instance(path.simplified());
+
+ error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser);
+
+ if (error) {
+ if (error != ERR_FILE_EOF) {
+ _printerr();
+ return Ref<PackedScene>();
+ } else {
+ return packed_scene;
+ }
+ }
+ } else {
+
+ error = ERR_FILE_CORRUPT;
+ _printerr();
+ return Ref<PackedScene>();
+ }
+ }
+
+ return packed_scene;
+}
+
Error ResourceInteractiveLoaderText::poll() {
if (error != OK)
@@ -364,231 +615,21 @@ Error ResourceInteractiveLoaderText::poll() {
return error;
}
- /*
- int add_name(const StringName& p_name);
- int add_value(const Variant& p_value);
- int add_node_path(const NodePath& p_path);
- int add_node(int p_parent,int p_owner,int p_type,int p_name, int p_instance);
- void add_node_property(int p_node,int p_name,int p_value);
- void add_node_group(int p_node,int p_group);
- void set_base_scene(int p_idx);
- void add_connection(int p_from,int p_to, int p_signal, int p_method, int p_flags,const Vector<int>& p_binds);
- void add_editable_instance(const NodePath& p_path);
-
- */
-
- int parent = -1;
- int owner = -1;
- int type = -1;
- int name = -1;
- int instance = -1;
- //int base_scene=-1;
-
- if (next_tag.fields.has("name")) {
- name = packed_scene->get_state()->add_name(next_tag.fields["name"]);
- }
-
- if (next_tag.fields.has("parent")) {
- NodePath np = next_tag.fields["parent"];
- np.prepend_period(); //compatible to how it manages paths internally
- parent = packed_scene->get_state()->add_node_path(np);
- }
-
- if (next_tag.fields.has("type")) {
- type = packed_scene->get_state()->add_name(next_tag.fields["type"]);
- } else {
- type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced
- }
-
- if (next_tag.fields.has("instance")) {
-
- instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]);
-
- if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) {
- packed_scene->get_state()->set_base_scene(instance);
- instance = -1;
- }
- }
-
- if (next_tag.fields.has("instance_placeholder")) {
-
- String path = next_tag.fields["instance_placeholder"];
-
- int path_v = packed_scene->get_state()->add_value(path);
-
- if (packed_scene->get_state()->get_node_count() == 0) {
- error = ERR_FILE_CORRUPT;
- error_text = "Instance Placeholder can't be used for inheritance.";
- _printerr();
- return error;
- }
-
- instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER;
- }
-
- if (next_tag.fields.has("owner")) {
- owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]);
- } else {
- if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1))
- owner = 0; //if no owner, owner is root
- }
-
- int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance);
-
- if (next_tag.fields.has("groups")) {
-
- Array groups = next_tag.fields["groups"];
- for (int i = 0; i < groups.size(); i++) {
- packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
- }
- }
-
- while (true) {
-
- String assign;
- Variant value;
-
- error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
-
- if (error) {
- if (error != ERR_FILE_EOF) {
- _printerr();
- } else {
- resource = packed_scene;
- if (!ResourceCache::has(res_path)) {
- packed_scene->set_path(res_path);
- }
- }
- return error;
- }
-
- if (assign != String()) {
- int nameidx = packed_scene->get_state()->add_name(assign);
- int valueidx = packed_scene->get_state()->add_value(value);
- packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx);
- //it's assignment
- } else if (next_tag.name != String()) {
-
- error = OK;
- return error;
- } else {
-
- resource = packed_scene;
- error = ERR_FILE_EOF;
- return error;
- }
- }
-
- return OK;
-
- } else if (next_tag.name == "connection") {
-
- if (!is_scene) {
-
- error_text += "found the 'connection' tag on a resource file!";
- _printerr();
- error = ERR_FILE_CORRUPT;
- return error;
- }
-
- if (!next_tag.fields.has("from")) {
- error = ERR_FILE_CORRUPT;
- error_text = "missing 'from' field fron connection tag";
- return error;
- }
-
- if (!next_tag.fields.has("to")) {
- error = ERR_FILE_CORRUPT;
- error_text = "missing 'to' field fron connection tag";
- return error;
- }
-
- if (!next_tag.fields.has("signal")) {
- error = ERR_FILE_CORRUPT;
- error_text = "missing 'signal' field fron connection tag";
- return error;
- }
-
- if (!next_tag.fields.has("method")) {
- error = ERR_FILE_CORRUPT;
- error_text = "missing 'method' field fron connection tag";
- return error;
- }
-
- NodePath from = next_tag.fields["from"];
- NodePath to = next_tag.fields["to"];
- StringName method = next_tag.fields["method"];
- StringName signal = next_tag.fields["signal"];
- int flags = CONNECT_PERSIST;
- Array binds;
-
- if (next_tag.fields.has("flags")) {
- flags = next_tag.fields["flags"];
- }
-
- if (next_tag.fields.has("binds")) {
- binds = next_tag.fields["binds"];
- }
-
- Vector<int> bind_ints;
- for (int i = 0; i < binds.size(); i++) {
- bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
- }
-
- packed_scene->get_state()->add_connection(
- packed_scene->get_state()->add_node_path(from.simplified()),
- packed_scene->get_state()->add_node_path(to.simplified()),
- packed_scene->get_state()->add_name(signal),
- packed_scene->get_state()->add_name(method),
- flags,
- bind_ints);
-
- error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
-
- if (error) {
- if (error != ERR_FILE_EOF) {
- _printerr();
- } else {
- resource = packed_scene;
- }
- }
-
- return error;
- } else if (next_tag.name == "editable") {
+ Ref<PackedScene> packed_scene = _parse_node_tag(rp);
- if (!is_scene) {
-
- error_text += "found the 'editable' tag on a resource file!";
- _printerr();
- error = ERR_FILE_CORRUPT;
+ if (!packed_scene.is_valid())
return error;
- }
- if (!next_tag.fields.has("path")) {
- error = ERR_FILE_CORRUPT;
- error_text = "missing 'path' field fron connection tag";
- _printerr();
- return error;
+ error = OK;
+ //get it here
+ resource = packed_scene;
+ if (!ResourceCache::has(res_path)) {
+ packed_scene->set_path(res_path);
}
- NodePath path = next_tag.fields["path"];
-
- packed_scene->get_state()->add_editable_instance(path.simplified());
-
- error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
-
- if (error) {
- if (error != ERR_FILE_EOF) {
- _printerr();
- } else {
- resource = packed_scene;
- }
- }
-
- return error;
+ return ERR_FILE_EOF;
} else {
-
error_text += "Unknown tag in file: " + next_tag.name;
_printerr();
error = ERR_FILE_CORRUPT;
@@ -804,7 +845,6 @@ void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag)
if (tag.name == "gd_scene") {
is_scene = true;
- packed_scene.instance();
} else if (tag.name == "gd_resource") {
if (!tag.fields.has("type")) {
@@ -846,6 +886,281 @@ void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag)
rp.userdata = this;
}
+static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) {
+
+ CharString utf8 = p_string.utf8();
+ if (p_bit_on_len) {
+ f->store_32(utf8.length() + 1 | 0x80000000);
+ } else {
+ f->store_32(utf8.length() + 1);
+ }
+ f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
+}
+
+Error ResourceInteractiveLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) {
+
+ if (error)
+ return error;
+
+ FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE);
+ if (!wf) {
+ return ERR_CANT_OPEN;
+ }
+
+ //save header compressed
+ static const uint8_t header[4] = { 'R', 'S', 'R', 'C' };
+ wf->store_buffer(header, 4);
+
+ wf->store_32(0); //endianness, little endian
+ wf->store_32(0); //64 bits file, false for now
+ wf->store_32(VERSION_MAJOR);
+ wf->store_32(VERSION_MINOR);
+ static const int save_format_version = 3; //use format version 3 for saving
+ wf->store_32(save_format_version);
+
+ bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type);
+ wf->store_64(0); //offset to import metadata, this is no longer used
+ for (int i = 0; i < 14; i++)
+ wf->store_32(0); // reserved
+
+ wf->store_32(0); //string table size, will not be in use
+ size_t ext_res_count_pos = wf->get_position();
+
+ wf->store_32(0); //zero ext resources, still parsing them
+
+ //go with external resources
+
+ DummyReadData dummy_read;
+ VariantParser::ResourceParser rp;
+ rp.ext_func = _parse_ext_resource_dummys;
+ rp.sub_func = _parse_sub_resource_dummys;
+ rp.userdata = &dummy_read;
+
+ while (next_tag.name == "ext_resource") {
+
+ if (!next_tag.fields.has("path")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'path' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ if (!next_tag.fields.has("type")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'type' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ if (!next_tag.fields.has("id")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'id' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ String path = next_tag.fields["path"];
+ String type = next_tag.fields["type"];
+ int index = next_tag.fields["id"];
+
+ bs_save_unicode_string(wf.f, type);
+ bs_save_unicode_string(wf.f, path);
+
+ int lindex = dummy_read.external_resources.size();
+ Ref<DummyResource> dr;
+ dr.instance();
+ dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external
+ dummy_read.external_resources[dr] = lindex;
+ dummy_read.rev_external_resources[index] = dr;
+
+ error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
+
+ if (error) {
+ _printerr();
+ return error;
+ }
+ }
+
+ // save external resource table
+ wf->seek(ext_res_count_pos);
+ wf->store_32(dummy_read.external_resources.size());
+ wf->seek_end();
+
+ //now, save resources to a separate file, for now
+
+ size_t sub_res_count_pos = wf->get_position();
+ wf->store_32(0); //zero sub resources, still parsing them
+
+ String temp_file = p_path + ".temp";
+ FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE);
+ if (!wf2) {
+ return ERR_CANT_OPEN;
+ }
+
+ Vector<size_t> local_offsets;
+ Vector<size_t> local_pointers_pos;
+
+ while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
+
+ String type;
+ int id = -1;
+ bool main_res;
+
+ if (next_tag.name == "sub_resource") {
+ if (!next_tag.fields.has("type")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'type' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ if (!next_tag.fields.has("id")) {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Missing 'index' in external resource tag";
+ _printerr();
+ return error;
+ }
+
+ type = next_tag.fields["type"];
+ id = next_tag.fields["id"];
+ main_res = false;
+ } else {
+ type = res_type;
+ id = 0; //used for last anyway
+ main_res = true;
+ }
+
+ local_offsets.push_back(wf2->get_position());
+
+ bs_save_unicode_string(wf, "local://" + itos(id));
+ local_pointers_pos.push_back(wf->get_position());
+ wf->store_64(0); //temp local offset
+
+ bs_save_unicode_string(wf2, type);
+ size_t propcount_ofs = wf2->get_position();
+ wf2->store_32(0);
+
+ int prop_count = 0;
+
+ while (true) {
+
+ String assign;
+ Variant value;
+
+ error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
+
+ if (error) {
+ if (main_res && error == ERR_FILE_EOF) {
+ next_tag.name = ""; //exit
+ break;
+ }
+
+ _printerr();
+ return error;
+ }
+
+ if (assign != String()) {
+
+ Map<StringName, int> empty_string_map; //unused
+ bs_save_unicode_string(wf2, assign, true);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+ prop_count++;
+
+ } else if (next_tag.name != String()) {
+
+ error = OK;
+ break;
+ } else {
+ error = ERR_FILE_CORRUPT;
+ error_text = "Premature end of file while parsing [sub_resource]";
+ _printerr();
+ return error;
+ }
+ }
+
+ wf2->seek(propcount_ofs);
+ wf2->store_32(prop_count);
+ wf2->seek_end();
+ }
+
+ if (next_tag.name == "node") {
+ //this is a node, must save one more!
+
+ if (!is_scene) {
+
+ error_text += "found the 'node' tag on a resource file!";
+ _printerr();
+ error = ERR_FILE_CORRUPT;
+ return error;
+ }
+
+ Ref<PackedScene> packed_scene = _parse_node_tag(rp);
+
+ if (!packed_scene.is_valid())
+ return error;
+
+ error = OK;
+ //get it here
+ List<PropertyInfo> props;
+ packed_scene->get_property_list(&props);
+
+ bs_save_unicode_string(wf, "local://0");
+ local_pointers_pos.push_back(wf->get_position());
+ wf->store_64(0); //temp local offset
+
+ local_offsets.push_back(wf2->get_position());
+ bs_save_unicode_string(wf2, "PackedScene");
+ size_t propcount_ofs = wf2->get_position();
+ wf2->store_32(0);
+
+ int prop_count = 0;
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+ if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
+ continue;
+
+ String name = E->get().name;
+ Variant value = packed_scene->get(name);
+
+ Map<StringName, int> empty_string_map; //unused
+ bs_save_unicode_string(wf2, name, true);
+ ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+ prop_count++;
+ }
+
+ wf2->seek(propcount_ofs);
+ wf2->store_32(prop_count);
+ wf2->seek_end();
+ }
+
+ wf2->close();
+
+ size_t offset_from = wf->get_position();
+ wf->seek(sub_res_count_pos); //plus one because the saved one
+ wf->store_32(local_offsets.size());
+
+ for (int i = 0; i < local_offsets.size(); i++) {
+ wf->seek(local_pointers_pos[i]);
+ wf->store_64(local_offsets[i] + offset_from);
+ }
+
+ wf->seek_end();
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file);
+ wf->store_buffer(data.ptr(), data.size());
+ {
+ DirAccessRef dar = DirAccess::open(temp_file.get_base_dir());
+ dar->remove(temp_file);
+ }
+
+ wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end
+
+ wf->close();
+
+ return OK;
+}
+
String ResourceInteractiveLoaderText::recognize(FileAccess *p_f) {
error = OK;
@@ -991,6 +1306,25 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const
return ria->rename_dependencies(f, p_path, p_map);
}
+Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) {
+
+ Error err;
+ FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
+
+ if (err != OK) {
+
+ ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
+ }
+
+ Ref<ResourceInteractiveLoaderText> ria = memnew(ResourceInteractiveLoaderText);
+ String path = p_src_path;
+ ria->local_path = ProjectSettings::get_singleton()->localize_path(path);
+ ria->res_path = ria->local_path;
+ //ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
+ ria->open(f);
+ return ria->save_as_binary(f, p_dst_path);
+}
+
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h
index a72a62037c..5d3c2004c1 100644
--- a/scene/resources/scene_format_text.h
+++ b/scene/resources/scene_format_text.h
@@ -78,9 +78,26 @@ class ResourceInteractiveLoaderText : public ResourceInteractiveLoader {
Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
- VariantParser::ResourceParser rp;
+ // for converter
+ class DummyResource : public Resource {
+ public:
+ };
- Ref<PackedScene> packed_scene;
+ struct DummyReadData {
+
+ Map<RES, int> external_resources;
+ Map<int, RES> rev_external_resources;
+ Set<RES> resource_set;
+ Map<int, RES> resource_map;
+ };
+
+ static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
+ static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
+
+ static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
+ static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
+
+ VariantParser::ResourceParser rp;
friend class ResourceFormatLoaderText;
@@ -89,6 +106,8 @@ class ResourceInteractiveLoaderText : public ResourceInteractiveLoader {
RES resource;
+ Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser);
+
public:
virtual void set_local_path(const String &p_local_path);
virtual Ref<Resource> get_resource();
@@ -102,6 +121,7 @@ public:
void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map);
+ Error save_as_binary(FileAccess *p_f, const String &p_path);
ResourceInteractiveLoaderText();
~ResourceInteractiveLoaderText();
};
@@ -115,6 +135,8 @@ public:
virtual String get_resource_type(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
+
+ static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path);
};
class ResourceFormatSaverTextInstance {
diff --git a/scene/resources/segment_shape_2d.cpp b/scene/resources/segment_shape_2d.cpp
index 7c7ec0d112..e8ef448e23 100644
--- a/scene/resources/segment_shape_2d.cpp
+++ b/scene/resources/segment_shape_2d.cpp
@@ -86,8 +86,8 @@ void SegmentShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "b"), "set_b", "get_b");
}
-SegmentShape2D::SegmentShape2D()
- : Shape2D(Physics2DServer::get_singleton()->segment_shape_create()) {
+SegmentShape2D::SegmentShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->segment_shape_create()) {
a = Vector2();
b = Vector2(0, 10);
@@ -145,8 +145,8 @@ real_t RayShape2D::get_length() const {
return length;
}
-RayShape2D::RayShape2D()
- : Shape2D(Physics2DServer::get_singleton()->ray_shape_create()) {
+RayShape2D::RayShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->ray_shape_create()) {
length = 20;
_update_shape();
diff --git a/scene/resources/shape_line_2d.cpp b/scene/resources/shape_line_2d.cpp
index d046ce876c..512ff3bc56 100644
--- a/scene/resources/shape_line_2d.cpp
+++ b/scene/resources/shape_line_2d.cpp
@@ -95,8 +95,8 @@ void LineShape2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "d"), "set_d", "get_d");
}
-LineShape2D::LineShape2D()
- : Shape2D(Physics2DServer::get_singleton()->line_shape_create()) {
+LineShape2D::LineShape2D() :
+ Shape2D(Physics2DServer::get_singleton()->line_shape_create()) {
normal = Vector2(0, -1);
d = 0;
diff --git a/scene/resources/sky_box.cpp b/scene/resources/sky_box.cpp
index 2ef20f67f5..a2c3f1f111 100644
--- a/scene/resources/sky_box.cpp
+++ b/scene/resources/sky_box.cpp
@@ -180,7 +180,7 @@ Ref<Image> ProceduralSky::_generate_sky() {
normal.normalize();
- float v_angle = Math::acos(normal.y);
+ float v_angle = Math::acos(CLAMP(normal.y, -1.0, 1.0));
Color color;
@@ -193,7 +193,7 @@ Ref<Image> ProceduralSky::_generate_sky() {
float c = v_angle / (Math_PI * 0.5);
color = sky_horizon_linear.linear_interpolate(sky_top_linear, Math::ease(1.0 - c, sky_curve));
- float sun_angle = Math::rad2deg(Math::acos(sun.dot(normal)));
+ float sun_angle = Math::rad2deg(Math::acos(CLAMP(sun.dot(normal), -1.0, 1.0)));
if (sun_angle < sun_angle_min) {
color = color.blend(sun_color);
diff --git a/scene/resources/sphere_shape.cpp b/scene/resources/sphere_shape.cpp
index 00203c2b4b..042988dcd3 100644
--- a/scene/resources/sphere_shape.cpp
+++ b/scene/resources/sphere_shape.cpp
@@ -80,8 +80,8 @@ void SphereShape::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_radius", "get_radius");
}
-SphereShape::SphereShape()
- : Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_SPHERE)) {
+SphereShape::SphereShape() :
+ Shape(PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_SPHERE)) {
set_radius(1.0);
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index bf89e704bc..d8600e041d 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -101,6 +101,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) {
vtx.color = last_color;
vtx.normal = last_normal;
vtx.uv = last_uv;
+ vtx.uv2 = last_uv2;
vtx.weights = last_weights;
vtx.bones = last_bones;
vtx.tangent = last_tangent.normal;
@@ -401,7 +402,7 @@ Array SurfaceTool::commit_to_arrays() {
return a;
}
-Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) {
+Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing, uint32_t p_flags) {
Ref<ArrayMesh> mesh;
if (p_existing.is_valid())
@@ -418,7 +419,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) {
Array a = commit_to_arrays();
- mesh->add_surface_from_arrays(primitive, a);
+ mesh->add_surface_from_arrays(primitive, a, Array(), p_flags);
if (material.is_valid())
mesh->surface_set_material(surface, material);
@@ -482,6 +483,113 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List<
_create_list_from_arrays(arr, r_vertex, r_index, lformat);
}
+Vector<SurfaceTool::Vertex> SurfaceTool::create_vertex_array_from_triangle_arrays(const Array &p_arrays) {
+
+ Vector<SurfaceTool::Vertex> ret;
+
+ PoolVector<Vector3> varr = p_arrays[VS::ARRAY_VERTEX];
+ PoolVector<Vector3> narr = p_arrays[VS::ARRAY_NORMAL];
+ PoolVector<float> tarr = p_arrays[VS::ARRAY_TANGENT];
+ PoolVector<Color> carr = p_arrays[VS::ARRAY_COLOR];
+ PoolVector<Vector2> uvarr = p_arrays[VS::ARRAY_TEX_UV];
+ PoolVector<Vector2> uv2arr = p_arrays[VS::ARRAY_TEX_UV2];
+ PoolVector<int> barr = p_arrays[VS::ARRAY_BONES];
+ PoolVector<float> warr = p_arrays[VS::ARRAY_WEIGHTS];
+
+ int vc = varr.size();
+
+ if (vc == 0)
+ return ret;
+ int lformat = 0;
+
+ PoolVector<Vector3>::Read rv;
+ if (varr.size()) {
+ lformat |= VS::ARRAY_FORMAT_VERTEX;
+ rv = varr.read();
+ }
+ PoolVector<Vector3>::Read rn;
+ if (narr.size()) {
+ lformat |= VS::ARRAY_FORMAT_NORMAL;
+ rn = narr.read();
+ }
+ PoolVector<float>::Read rt;
+ if (tarr.size()) {
+ lformat |= VS::ARRAY_FORMAT_TANGENT;
+ rt = tarr.read();
+ }
+ PoolVector<Color>::Read rc;
+ if (carr.size()) {
+ lformat |= VS::ARRAY_FORMAT_COLOR;
+ rc = carr.read();
+ }
+
+ PoolVector<Vector2>::Read ruv;
+ if (uvarr.size()) {
+ lformat |= VS::ARRAY_FORMAT_TEX_UV;
+ ruv = uvarr.read();
+ }
+
+ PoolVector<Vector2>::Read ruv2;
+ if (uv2arr.size()) {
+ lformat |= VS::ARRAY_FORMAT_TEX_UV2;
+ ruv2 = uv2arr.read();
+ }
+
+ PoolVector<int>::Read rb;
+ if (barr.size()) {
+ lformat |= VS::ARRAY_FORMAT_BONES;
+ rb = barr.read();
+ }
+
+ PoolVector<float>::Read rw;
+ if (warr.size()) {
+ lformat |= VS::ARRAY_FORMAT_WEIGHTS;
+ rw = warr.read();
+ }
+
+ for (int i = 0; i < vc; i++) {
+
+ Vertex v;
+ if (lformat & VS::ARRAY_FORMAT_VERTEX)
+ v.vertex = varr[i];
+ if (lformat & VS::ARRAY_FORMAT_NORMAL)
+ v.normal = narr[i];
+ if (lformat & VS::ARRAY_FORMAT_TANGENT) {
+ Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]);
+ v.tangent = p.normal;
+ v.binormal = p.normal.cross(v.tangent).normalized() * p.d;
+ }
+ if (lformat & VS::ARRAY_FORMAT_COLOR)
+ v.color = carr[i];
+ if (lformat & VS::ARRAY_FORMAT_TEX_UV)
+ v.uv = uvarr[i];
+ if (lformat & VS::ARRAY_FORMAT_TEX_UV2)
+ v.uv2 = uv2arr[i];
+ if (lformat & VS::ARRAY_FORMAT_BONES) {
+ Vector<int> b;
+ b.resize(4);
+ b[0] = barr[i * 4 + 0];
+ b[1] = barr[i * 4 + 1];
+ b[2] = barr[i * 4 + 2];
+ b[3] = barr[i * 4 + 3];
+ v.bones = b;
+ }
+ if (lformat & VS::ARRAY_FORMAT_WEIGHTS) {
+ Vector<float> w;
+ w.resize(4);
+ w[0] = warr[i * 4 + 0];
+ w[1] = warr[i * 4 + 1];
+ w[2] = warr[i * 4 + 2];
+ w[3] = warr[i * 4 + 3];
+ v.weights = w;
+ }
+
+ ret.push_back(v);
+ }
+
+ return ret;
+}
+
void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) {
PoolVector<Vector3> varr = arr[VS::ARRAY_VERTEX];
@@ -882,7 +990,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_from", "existing", "surface"), &SurfaceTool::create_from);
ClassDB::bind_method(D_METHOD("append_from", "existing", "surface", "transform"), &SurfaceTool::append_from);
- ClassDB::bind_method(D_METHOD("commit", "existing"), &SurfaceTool::commit, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("commit", "existing", "flags"), &SurfaceTool::commit, DEFVAL(Variant()), DEFVAL(Mesh::ARRAY_COMPRESS_DEFAULT));
}
SurfaceTool::SurfaceTool() {
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index cdaac643de..b180ffe260 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -127,10 +127,11 @@ public:
List<Vertex> &get_vertex_array() { return vertex_array; }
void create_from_triangle_arrays(const Array &p_arrays);
+ static Vector<Vertex> create_vertex_array_from_triangle_arrays(const Array &p_arrays);
Array commit_to_arrays();
void create_from(const Ref<Mesh> &p_existing, int p_surface);
void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform);
- Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>());
+ Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>(), uint32_t p_flags = Mesh::ARRAY_COMPRESS_DEFAULT);
SurfaceTool();
};
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 6fab597d2b..bd6b917d4e 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -83,7 +83,7 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
}
}
} else if (what == "occluder_map") {
- tile_map[id].autotile_data.ocludder_map.clear();
+ tile_map[id].autotile_data.occluder_map.clear();
Array p = p_value;
Vector2 last_coord;
while (p.size() > 0) {
@@ -194,7 +194,7 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = p;
} else if (what == "occluder_map") {
Array p;
- for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = tile_map[id].autotile_data.ocludder_map.front(); E; E = E->next()) {
+ for (Map<Vector2, Ref<OccluderPolygon2D> >::Element *E = tile_map[id].autotile_data.occluder_map.front(); E; E = E->next()) {
p.push_back(E->key());
p.push_back(E->value());
}
@@ -643,20 +643,20 @@ Ref<OccluderPolygon2D> TileSet::tile_get_light_occluder(int p_id) const {
void TileSet::autotile_set_light_occluder(int p_id, const Ref<OccluderPolygon2D> &p_light_occluder, const Vector2 &p_coord) {
ERR_FAIL_COND(!tile_map.has(p_id));
if (p_light_occluder.is_null()) {
- if (tile_map[p_id].autotile_data.ocludder_map.has(p_coord)) {
- tile_map[p_id].autotile_data.ocludder_map.erase(p_coord);
+ if (tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
+ tile_map[p_id].autotile_data.occluder_map.erase(p_coord);
}
} else {
- tile_map[p_id].autotile_data.ocludder_map[p_coord] = p_light_occluder;
+ tile_map[p_id].autotile_data.occluder_map[p_coord] = p_light_occluder;
}
}
Ref<OccluderPolygon2D> TileSet::autotile_get_light_occluder(int p_id, const Vector2 &p_coord) const {
ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<OccluderPolygon2D>());
- if (!tile_map[p_id].autotile_data.ocludder_map.has(p_coord)) {
+ if (!tile_map[p_id].autotile_data.occluder_map.has(p_coord)) {
return Ref<OccluderPolygon2D>();
} else {
- return tile_map[p_id].autotile_data.ocludder_map[p_coord];
+ return tile_map[p_id].autotile_data.occluder_map[p_coord];
}
}
@@ -688,7 +688,7 @@ const Map<Vector2, Ref<OccluderPolygon2D> > &TileSet::autotile_get_light_oclusio
static Map<Vector2, Ref<OccluderPolygon2D> > dummy;
ERR_FAIL_COND_V(!tile_map.has(p_id), dummy);
- return tile_map[p_id].autotile_data.ocludder_map;
+ return tile_map[p_id].autotile_data.occluder_map;
}
void TileSet::autotile_set_navigation_polygon(int p_id, const Ref<NavigationPolygon> &p_navigation_polygon, const Vector2 &p_coord) {
@@ -932,8 +932,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("find_tile_by_name", "name"), &TileSet::find_tile_by_name);
ClassDB::bind_method(D_METHOD("get_tiles_ids"), &TileSet::_get_tiles_ids);
- BIND_VMETHOD(MethodInfo("_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
- BIND_VMETHOD(MethodInfo("_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
+ BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_tile_bound", PropertyInfo(Variant::INT, "drawn_id"), PropertyInfo(Variant::INT, "neighbor_id")));
+ BIND_VMETHOD(MethodInfo(Variant::VECTOR2, "_forward_subtile_selection", PropertyInfo(Variant::INT, "autotile_id"), PropertyInfo(Variant::INT, "bitmask"), PropertyInfo(Variant::OBJECT, "tilemap", PROPERTY_HINT_NONE, "TileMap"), PropertyInfo(Variant::VECTOR2, "tile_location")));
BIND_ENUM_CONSTANT(BITMASK_2X2);
BIND_ENUM_CONSTANT(BITMASK_3X3);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 18b62c778d..deac583f62 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -76,13 +76,14 @@ public:
Size2 size;
Vector2 icon_coord;
Map<Vector2, uint16_t> flags;
- Map<Vector2, Ref<OccluderPolygon2D> > ocludder_map;
+ Map<Vector2, Ref<OccluderPolygon2D> > occluder_map;
Map<Vector2, Ref<NavigationPolygon> > navpoly_map;
Map<Vector2, int> priority_map;
// Default size to prevent invalid value
- explicit AutotileData()
- : size(64, 64), icon_coord(0, 0) {
+ explicit AutotileData() :
+ size(64, 64),
+ icon_coord(0, 0) {
bitmask_mode = BITMASK_2X2;
}
};
@@ -106,8 +107,9 @@ private:
AutotileData autotile_data;
// Default modulate for back-compat
- explicit TileData()
- : modulate(1, 1, 1), is_autotile(false) {}
+ explicit TileData() :
+ modulate(1, 1, 1),
+ is_autotile(false) {}
};
Map<int, TileData> tile_map;