summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/bind/core_bind.cpp24
-rw-r--r--core/bind/core_bind.h8
-rw-r--r--core/class_db.cpp14
-rw-r--r--core/class_db.h3
-rw-r--r--core/image.cpp59
-rw-r--r--core/image.h1
-rw-r--r--core/math/matrix3.h6
-rw-r--r--core/math/transform.cpp13
-rw-r--r--core/math/transform.h10
-rw-r--r--core/os/input_event.cpp39
-rw-r--r--core/os/input_event.h4
-rw-r--r--core/reference.h2
-rw-r--r--core/ustring.cpp2
-rw-r--r--core/variant_call.cpp41
-rw-r--r--doc/base/classes.xml56
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp252
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h7
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp62
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h5
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp33
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h6
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/gles3/shader_gles3.cpp2
-rw-r--r--drivers/gles3/shaders/canvas.glsl126
-rw-r--r--drivers/gles3/shaders/copy.glsl5
-rw-r--r--drivers/gles3/shaders/particles.glsl9
-rw-r--r--editor/editor_node.cpp63
-rw-r--r--editor/icons/icon_audio_player.pngbin443 -> 0 bytes
-rw-r--r--editor/import/editor_import_plugin.cpp2
-rw-r--r--editor/import/editor_import_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp935
-rw-r--r--editor/plugins/curve_editor_plugin.h128
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp303
-rw-r--r--editor/plugins/particles_2d_editor_plugin.h18
-rw-r--r--editor/plugins/shader_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp12
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp3
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp17
-rw-r--r--editor/project_export.cpp2
-rw-r--r--editor/script_create_dialog.cpp2
-rw-r--r--main/input_default.cpp2
-rw-r--r--modules/gdscript/gd_editor.cpp6
-rw-r--r--platform/osx/export/export.cpp505
-rw-r--r--platform/windows/godot.icobin370070 -> 110755 bytes
-rw-r--r--scene/2d/animated_sprite.cpp1
-rw-r--r--scene/2d/animated_sprite.h2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp12
-rw-r--r--scene/2d/canvas_item.cpp214
-rw-r--r--scene/2d/canvas_item.h94
-rw-r--r--scene/2d/collision_object_2d.cpp339
-rw-r--r--scene/2d/collision_object_2d.h62
-rw-r--r--scene/2d/collision_polygon_2d.cpp186
-rw-r--r--scene/2d/collision_polygon_2d.h32
-rw-r--r--scene/2d/collision_shape_2d.cpp176
-rw-r--r--scene/2d/collision_shape_2d.h26
-rw-r--r--scene/2d/particles_2d.cpp1185
-rw-r--r--scene/2d/particles_2d.h247
-rw-r--r--scene/2d/physics_body_2d.cpp452
-rw-r--r--scene/2d/physics_body_2d.h86
-rw-r--r--scene/3d/mesh_instance.cpp2
-rw-r--r--scene/3d/particles.cpp222
-rw-r--r--scene/3d/particles.h12
-rw-r--r--scene/3d/path.cpp66
-rw-r--r--scene/3d/scenario_fx.cpp35
-rw-r--r--scene/3d/scenario_fx.h4
-rw-r--r--scene/3d/sprite_3d.cpp8
-rw-r--r--scene/3d/sprite_3d.h1
-rw-r--r--scene/3d/visual_instance.cpp2
-rw-r--r--scene/gui/input_action.cpp2
-rw-r--r--scene/gui/patch_9_rect.cpp36
-rw-r--r--scene/gui/patch_9_rect.h17
-rw-r--r--scene/gui/popup_menu.cpp21
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/main/viewport.cpp11
-rw-r--r--scene/register_scene_types.cpp7
-rw-r--r--scene/resources/curve.cpp359
-rw-r--r--scene/resources/curve.h72
-rw-r--r--scene/resources/environment.cpp3
-rw-r--r--scene/resources/mesh.cpp11
-rw-r--r--scene/resources/mesh.h1
-rw-r--r--scene/resources/texture.cpp242
-rw-r--r--scene/resources/texture.h27
-rw-r--r--servers/physics_2d/area_pair_2d_sw.cpp15
-rw-r--r--servers/physics_2d/body_2d_sw.cpp2
-rw-r--r--servers/physics_2d/body_2d_sw.h15
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp15
-rw-r--r--servers/physics_2d/collision_object_2d_sw.cpp3
-rw-r--r--servers/physics_2d/collision_object_2d_sw.h15
-rw-r--r--servers/physics_2d/physics_2d_server_sw.cpp52
-rw-r--r--servers/physics_2d/physics_2d_server_sw.h12
-rw-r--r--servers/physics_2d/physics_2d_server_wrap_mt.h11
-rw-r--r--servers/physics_2d/space_2d_sw.cpp107
-rw-r--r--servers/physics_2d_server.cpp11
-rw-r--r--servers/physics_2d_server.h13
-rw-r--r--servers/visual/rasterizer.h24
-rw-r--r--servers/visual/shader_types.cpp1
-rw-r--r--servers/visual/visual_server_canvas.cpp31
-rw-r--r--servers/visual/visual_server_canvas.h9
-rw-r--r--servers/visual/visual_server_raster.h4
-rw-r--r--servers/visual/visual_server_scene.cpp12
-rw-r--r--servers/visual/visual_server_scene.h1
-rw-r--r--servers/visual/visual_server_viewport.cpp68
-rw-r--r--servers/visual/visual_server_wrap_mt.h2
-rw-r--r--servers/visual_server.cpp8
-rw-r--r--servers/visual_server.h7
107 files changed, 4101 insertions, 3414 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index d81ccf0265..095f058ed9 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -31,6 +31,7 @@
#include "core/global_config.h"
#include "geometry.h"
+#include "io/file_access_compressed.h"
#include "io/file_access_encrypted.h"
#include "io/marshalls.h"
#include "os/keyboard.h"
@@ -1395,6 +1396,24 @@ Error _File::open_encrypted_pass(const String &p_path, int p_mode_flags, const S
return OK;
}
+Error _File::open_compressed(const String &p_path, int p_mode_flags, int p_compress_mode) {
+
+ FileAccessCompressed *fac = memnew(FileAccessCompressed);
+ Error err = OK;
+
+ fac->configure("GCPF", (Compression::Mode)p_compress_mode);
+
+ err = fac->_open(p_path, p_mode_flags);
+
+ if (err) {
+ memdelete(fac);
+ return err;
+ }
+
+ f = fac;
+ return OK;
+}
+
Error _File::open(const String &p_path, int p_mode_flags) {
close();
@@ -1700,6 +1719,7 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("open_encrypted", "path", "mode_flags", "key"), &_File::open_encrypted);
ClassDB::bind_method(D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &_File::open_encrypted_pass);
+ ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &_File::open_compressed, DEFVAL(0));
ClassDB::bind_method(D_METHOD("open", "path", "flags"), &_File::open);
ClassDB::bind_method(D_METHOD("close"), &_File::close);
@@ -1749,6 +1769,10 @@ void _File::_bind_methods() {
BIND_CONSTANT(WRITE);
BIND_CONSTANT(READ_WRITE);
BIND_CONSTANT(WRITE_READ);
+
+ BIND_CONSTANT(COMPRESSION_FASTLZ);
+ BIND_CONSTANT(COMPRESSION_DEFLATE);
+ BIND_CONSTANT(COMPRESSION_ZSTD);
}
_File::_File() {
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index a2fb6c966c..87d84c0732 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -31,6 +31,7 @@
#define CORE_BIND_H
#include "image.h"
+#include "io/compression.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
#include "os/dir_access.h"
@@ -366,8 +367,15 @@ public:
WRITE_READ = 7,
};
+ enum CompressionMode {
+ COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
+ COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
+ COMPRESSION_ZSTD = Compression::MODE_ZSTD
+ };
+
Error open_encrypted(const String &p_path, int p_mode_flags, const Vector<uint8_t> &p_key);
Error open_encrypted_pass(const String &p_path, int p_mode_flags, const String &p_pass);
+ Error open_compressed(const String &p_path, int p_mode_flags, int p_compress_mode = 0);
Error open(const String &p_path, int p_mode_flags); ///< open a file
void close(); ///< close a file
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 1fe02c8cd9..6b8c290a99 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -497,7 +497,7 @@ void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherit
}
}
-void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) {
+void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) {
OBJTYPE_RLOCK;
@@ -528,6 +528,9 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b
minfo.name = E->get();
minfo.id = method->get_method_id();
+ if (p_exclude_from_properties && type->methods_in_properties.has(minfo.name))
+ continue;
+
for (int i = 0; i < method->get_argument_count(); i++) {
//Variant::Type t=method->get_argument_type(i);
@@ -802,7 +805,14 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
OBJTYPE_WLOCK
type->property_list.push_back(p_pinfo);
-
+#ifdef DEBUG_METHODS_ENABLED
+ if (mb_get) {
+ type->methods_in_properties.insert(p_getter);
+ }
+ if (mb_set) {
+ type->methods_in_properties.insert(p_setter);
+ }
+#endif
PropertySetGet psg;
psg.setter = p_setter;
psg.getter = p_getter;
diff --git a/core/class_db.h b/core/class_db.h
index 547068da5f..4f00a16e91 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -139,6 +139,7 @@ public:
#ifdef DEBUG_METHODS_ENABLED
List<StringName> constant_order;
List<StringName> method_order;
+ Set<StringName> methods_in_properties;
List<MethodInfo> virtual_methods;
StringName category;
#endif
@@ -486,7 +487,7 @@ public:
static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false);
static void set_method_flags(StringName p_class, StringName p_method, int p_flags);
- static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
+ static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static MethodBind *get_method(StringName p_class, StringName p_name);
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true);
diff --git a/core/image.cpp b/core/image.cpp
index ec21260b19..023a058667 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -1646,6 +1646,62 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
}
}
+void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest) {
+
+ ERR_FAIL_COND(p_src.is_null());
+ ERR_FAIL_COND(p_mask.is_null());
+ int dsize = data.size();
+ int srcdsize = p_src->data.size();
+ int maskdsize = p_mask->data.size();
+ ERR_FAIL_COND(dsize == 0);
+ ERR_FAIL_COND(srcdsize == 0);
+ ERR_FAIL_COND(maskdsize == 0);
+ ERR_FAIL_COND(p_src->width != p_mask->width);
+ ERR_FAIL_COND(p_src->height != p_mask->height);
+ ERR_FAIL_COND(format != p_src->format);
+
+ Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
+ if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0)
+ return;
+
+ Rect2i dest_rect = Rect2i(0, 0, width, height).clip(Rect2i(p_dest, clipped_src_rect.size));
+
+ PoolVector<uint8_t>::Write wp = data.write();
+ uint8_t *dst_data_ptr = wp.ptr();
+
+ PoolVector<uint8_t>::Read rp = p_src->data.read();
+ const uint8_t *src_data_ptr = rp.ptr();
+
+ int pixel_size = get_format_pixel_size(format);
+
+ Ref<Image> msk = p_mask;
+ msk->lock();
+
+ for (int i = 0; i < dest_rect.size.y; i++) {
+
+ for (int j = 0; j < dest_rect.size.x; j++) {
+
+ int src_x = clipped_src_rect.position.x + j;
+ int src_y = clipped_src_rect.position.y + i;
+
+ if (msk->get_pixel(src_x, src_y).a != 0) {
+
+ int dst_x = dest_rect.position.x + j;
+ int dst_y = dest_rect.position.y + i;
+
+ const uint8_t *src = &src_data_ptr[(src_y * p_src->width + src_x) * pixel_size];
+ uint8_t *dst = &dst_data_ptr[(dst_y * width + dst_x) * pixel_size];
+
+ for (int k = 0; k < pixel_size; k++) {
+ dst[k] = src[k];
+ }
+ }
+ }
+ }
+
+ msk->unlock();
+}
+
void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest) {
ERR_FAIL_COND(p_src.is_null());
@@ -2167,7 +2223,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mipmap_offset", "mipmap"), &Image::get_mipmap_offset);
- ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL("false"));
+ ClassDB::bind_method(D_METHOD("resize_to_po2", "square"), &Image::resize_to_po2, DEFVAL(false));
ClassDB::bind_method(D_METHOD("resize", "width", "height", "interpolation"), &Image::resize, DEFVAL(INTERPOLATE_BILINEAR));
ClassDB::bind_method(D_METHOD("shrink_x2"), &Image::shrink_x2);
ClassDB::bind_method(D_METHOD("expand_x2_hq2x"), &Image::expand_x2_hq2x);
@@ -2199,6 +2255,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy);
ClassDB::bind_method(D_METHOD("blit_rect", "src:Image", "src_rect", "dst"), &Image::blit_rect);
+ ClassDB::bind_method(D_METHOD("blit_rect_mask", "src:Image", "mask:Image", "src_rect", "dst"), &Image::blit_rect_mask);
ClassDB::bind_method(D_METHOD("blend_rect", "src:Image", "src_rect", "dst"), &Image::blend_rect);
ClassDB::bind_method(D_METHOD("blend_rect_mask", "src:Image", "mask:Image", "src_rect", "dst"), &Image::blend_rect_mask);
ClassDB::bind_method(D_METHOD("fill", "color"), &Image::fill);
diff --git a/core/image.h b/core/image.h
index 3323afdc4b..e523f703fa 100644
--- a/core/image.h
+++ b/core/image.h
@@ -283,6 +283,7 @@ public:
void normalmap_to_xy();
void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
+ void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest);
void blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
void blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest);
void fill(const Color &c);
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index c3eeb1f705..8897c692f7 100644
--- a/core/math/matrix3.h
+++ b/core/math/matrix3.h
@@ -145,6 +145,12 @@ public:
elements[2][1] = zy;
elements[2][2] = zz;
}
+ _FORCE_INLINE_ void set(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z) {
+
+ set_axis(0, p_x);
+ set_axis(1, p_y);
+ set_axis(2, p_z);
+ }
_FORCE_INLINE_ Vector3 get_column(int i) const {
return Vector3(elements[0][i], elements[1][i], elements[2][i]);
diff --git a/core/math/transform.cpp b/core/math/transform.cpp
index e53e6cf519..7a50214596 100644
--- a/core/math/transform.cpp
+++ b/core/math/transform.cpp
@@ -82,7 +82,10 @@ Transform Transform::looking_at(const Vector3 &p_target, const Vector3 &p_up) co
}
void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) {
-
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND(p_eye == p_target);
+ ERR_FAIL_COND(p_up.length() == 0);
+#endif
// Reference: MESA source code
Vector3 v_x, v_y, v_z;
@@ -96,6 +99,9 @@ void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const
v_y = p_up;
v_x = v_y.cross(v_z);
+#ifdef MATH_CHECKS
+ ERR_FAIL_COND(v_x.length() == 0);
+#endif
/* Recompute Y = Z cross X */
v_y = v_z.cross(v_x);
@@ -103,9 +109,8 @@ void Transform::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const
v_x.normalize();
v_y.normalize();
- basis.set_axis(0, v_x);
- basis.set_axis(1, v_y);
- basis.set_axis(2, v_z);
+ basis.set(v_x, v_y, v_z);
+
origin = p_eye;
}
diff --git a/core/math/transform.h b/core/math/transform.h
index c39ea7826f..48467f2ed7 100644
--- a/core/math/transform.h
+++ b/core/math/transform.h
@@ -97,15 +97,7 @@ public:
void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) {
- basis.elements[0][0] = xx;
- basis.elements[0][1] = xy;
- basis.elements[0][2] = xz;
- basis.elements[1][0] = yx;
- basis.elements[1][1] = yy;
- basis.elements[1][2] = yz;
- basis.elements[2][0] = zx;
- basis.elements[2][1] = zy;
- basis.elements[2][2] = zz;
+ basis.set(xx, xy, xz, yx, yy, yz, zx, zy, zz);
origin.x = tx;
origin.y = ty;
origin.z = tz;
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index dbdf9628e3..e60f588be3 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -89,6 +89,11 @@ bool InputEvent::action_match(const Ref<InputEvent> &p_event) const {
return false;
}
+bool InputEvent::shortcut_match(const Ref<InputEvent> &p_event) const {
+
+ return false;
+}
+
bool InputEvent::is_action_type() const {
return false;
@@ -130,6 +135,7 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_text"), &InputEvent::as_text);
ClassDB::bind_method(D_METHOD("action_match", "event:InputEvent"), &InputEvent::action_match);
+ ClassDB::bind_method(D_METHOD("shortcut_match", "event:InputEvent"), &InputEvent::shortcut_match);
ClassDB::bind_method(D_METHOD("is_action_type"), &InputEvent::is_action_type);
@@ -276,6 +282,27 @@ uint32_t InputEventKey::get_scancode_with_modifiers() const {
return sc;
}
+String InputEventKey::as_text() const {
+
+ String kc = keycode_get_string(scancode);
+ if (kc == String())
+ return kc;
+
+ if (get_metakey()) {
+ kc = "Meta+" + kc;
+ }
+ if (get_alt()) {
+ kc = "Alt+" + kc;
+ }
+ if (get_shift()) {
+ kc = "Shift+" + kc;
+ }
+ if (get_control()) {
+ kc = "Ctrl+" + kc;
+ }
+ return kc;
+}
+
bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
Ref<InputEventKey> key = p_event;
@@ -288,6 +315,18 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
return get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
}
+bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const {
+
+ Ref<InputEventKey> key = p_event;
+ if (key.is_null())
+ return false;
+
+ uint32_t code = get_scancode_with_modifiers();
+ uint32_t event_code = key->get_scancode_with_modifiers();
+
+ return code == event_code;
+}
+
void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventKey::set_pressed);
diff --git a/core/os/input_event.h b/core/os/input_event.h
index 6a694df345..b120d4b840 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -165,6 +165,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const;
InputEvent();
@@ -243,9 +244,12 @@ public:
uint32_t get_scancode_with_modifiers() const;
virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const { return true; }
+ virtual String as_text() const;
+
InputEventKey();
};
diff --git a/core/reference.h b/core/reference.h
index afc097817a..4e2d6c36c0 100644
--- a/core/reference.h
+++ b/core/reference.h
@@ -344,7 +344,7 @@ struct PtrToArg<const Ref<T> &> {
_FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) {
- return Ref<T>(reinterpret_cast<const T *>(p_ptr));
+ return Ref<T>((T *)p_ptr);
}
};
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 6a93d7789e..7ccf7fd209 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -65,7 +65,7 @@ bool CharString::operator<(const CharString &p_right) const {
}
const char *this_str = get_data();
- const char *that_str = get_data();
+ const char *that_str = p_right.get_data();
while (true) {
if (*that_str == 0 && *this_str == 0)
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 9ead727a80..6936a362e1 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -30,6 +30,7 @@
#include "variant.h"
#include "core_string_names.h"
+#include "io/compression.h"
#include "object.h"
#include "os/os.h"
#include "script_language.h"
@@ -509,6 +510,44 @@ struct _VariantCall {
r_ret = s;
}
+ static void _call_PoolByteArray_compress(Variant &r_ret, Variant &p_self, const Variant **p_args) {
+
+ PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem);
+ PoolByteArray compressed;
+ Compression::Mode mode = (Compression::Mode)(int)(*p_args[0]);
+
+ compressed.resize(Compression::get_max_compressed_buffer_size(ba->size()));
+ int result = Compression::compress(compressed.write().ptr(), ba->read().ptr(), ba->size(), mode);
+
+ result = result >= 0 ? result : 0;
+ compressed.resize(result);
+
+ r_ret = compressed;
+ }
+
+ static void _call_PoolByteArray_decompress(Variant &r_ret, Variant &p_self, const Variant **p_args) {
+
+ PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem);
+ PoolByteArray decompressed;
+ Compression::Mode mode = (Compression::Mode)(int)(*p_args[1]);
+
+ int buffer_size = (int)(*p_args[0]);
+
+ if (buffer_size < 0) {
+ r_ret = decompressed;
+ ERR_EXPLAIN("Decompression buffer size is less than zero");
+ ERR_FAIL();
+ }
+
+ decompressed.resize(buffer_size);
+ int result = Compression::decompress(decompressed.write().ptr(), buffer_size, ba->read().ptr(), ba->size(), mode);
+
+ result = result >= 0 ? result : 0;
+ decompressed.resize(result);
+
+ r_ret = decompressed;
+ }
+
VCALL_LOCALMEM0R(PoolByteArray, size);
VCALL_LOCALMEM2(PoolByteArray, set);
VCALL_LOCALMEM1R(PoolByteArray, get);
@@ -1552,6 +1591,8 @@ void register_variant_methods() {
ADDFUNC0(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_ascii, varray());
ADDFUNC0(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_utf8, varray());
+ ADDFUNC1(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, compress, INT, "compression_mode", varray(0));
+ ADDFUNC2(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0));
ADDFUNC0(POOL_INT_ARRAY, INT, PoolIntArray, size, varray());
ADDFUNC2(POOL_INT_ARRAY, NIL, PoolIntArray, set, INT, "idx", INT, "integer", varray());
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index fd3d629505..c2c8d3e8dd 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -15299,6 +15299,19 @@
Open the file for writing or reading, depending on the flags.
</description>
</method>
+ <method name="open_compressed">
+ <return type="int">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <argument index="1" name="mode_flags" type="int">
+ </argument>
+ <argument index="2" name="compression_mode" type="int" default="0">
+ </argument>
+ <description>
+ Open a compressed file for reading or writing. The compression_mode can be set as one of the COMPRESSION_* constants.
+ </description>
+ </method>
<method name="open_encrypted">
<return type="int">
</return>
@@ -15445,6 +15458,15 @@
<constant name="WRITE_READ" value="7">
Open the file for reading and writing. Create it if the file not exists and truncate if it exists.
</constant>
+ <constant name="COMPRESSION_FASTLZ" value="0">
+ Use the FastLZ compression method.
+ </constant>
+ <constant name="COMPRESSION_DEFLATE" value="1">
+ Use the Deflate compression method.
+ </constant>
+ <constant name="COMPRESSION_ZSTD" value="2">
+ Use the Zstd compression method.
+ </constant>
</constants>
</class>
<class name="FileDialog" inherits="ConfirmationDialog" category="Core">
@@ -33764,6 +33786,26 @@
Append an [PoolByteArray] at the end of this array.
</description>
</method>
+ <method name="compress">
+ <return type="PoolByteArray">
+ </return>
+ <argument index="0" name="compression_mode" type="int" default="0">
+ </argument>
+ <description>
+ Returns a new [PoolByteArray] with the data compressed. The compression mode can be set using one of the COMPRESS_* constants of [File].
+ </description>
+ </method>
+ <method name="decompress">
+ <return type="PoolByteArray">
+ </return>
+ <argument index="0" name="buffer_size" type="int">
+ </argument>
+ <argument index="1" name="compression_mode" type="int" default="0">
+ </argument>
+ <description>
+ Returns a new [PoolByteArray] with the data decompressed. The buffer_size should be set as the size of the uncompressed data. The compression mode can be set using one of the COMPRESS_* constants of [File].
+ </description>
+ </method>
<method name="get_string_from_ascii">
<return type="String">
</return>
@@ -34682,6 +34724,13 @@
Returns a boolean that indicates whether or not the PopupMenu will hide on item selection.
</description>
</method>
+ <method name="is_hide_on_checkable_item_selection">
+ <return type="bool">
+ </return>
+ <description>
+ Returns a boolean that indicates whether or not the PopupMenu will hide on checkable item selection.
+ </description>
+ </method>
<method name="is_item_checkable" qualifiers="const">
<return type="bool">
</return>
@@ -34732,6 +34781,13 @@
Sets whether or not the PopupMenu will hide on item selection.
</description>
</method>
+ <method name="set_hide_on_checkable_item_selection">
+ <argument index="0" name="enable" type="bool">
+ </argument>
+ <description>
+ Sets whether or not the PopupMenu will hide on checkable item selection.
+ </description>
+ </method>
<method name="set_item_ID">
<argument index="0" name="idx" type="int">
</argument>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index a8895f7bfc..c71bf22965 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "rasterizer_canvas_gles3.h"
+#include "servers/visual/visual_server_raster.h"
#include "global_config.h"
#include "os/os.h"
@@ -164,6 +165,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF5, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
state.canvas_shader.set_custom_shader(0);
state.canvas_shader.bind();
@@ -178,6 +180,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, state.canvas_item_ubo);
glBindVertexArray(data.canvas_quad_array);
state.using_texture_rect = true;
+ state.using_ninepatch = false;
}
void RasterizerCanvasGLES3::canvas_end() {
@@ -186,6 +189,7 @@ void RasterizerCanvasGLES3::canvas_end() {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0);
state.using_texture_rect = false;
+ state.using_ninepatch = false;
}
RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) {
@@ -258,9 +262,9 @@ RasterizerStorageGLES3::Texture *RasterizerCanvasGLES3::_bind_canvas_texture(con
return tex_return;
}
-void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
+void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepatch) {
- if (state.using_texture_rect == p_enable)
+ if (state.using_texture_rect == p_enable && state.using_ninepatch == p_ninepatch)
return;
if (p_enable) {
@@ -272,6 +276,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, p_ninepatch && p_enable);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_TEXTURE_RECT, p_enable);
state.canvas_shader.bind();
state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate);
@@ -279,6 +284,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable) {
state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix);
state.using_texture_rect = p_enable;
+ state.using_ninepatch = p_ninepatch;
}
void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
@@ -493,79 +499,41 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(c);
- _set_texture_rect_mode(true);
+ _set_texture_rect_mode(true, true);
glVertexAttrib4f(VS::ARRAY_COLOR, np->color.r, np->color.g, np->color.b, np->color.a);
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(np->texture, np->normal_map);
- if (!texture) {
-
- glVertexAttrib4f(1, np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
- glVertexAttrib4f(2, 0, 0, 1, 1);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- continue;
- }
-
- Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
-
- state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
- state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false);
-
-#define DSTRECT(m_x, m_y, m_w, m_h) state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(m_x, m_y, m_w, m_h))
-#define SRCRECT(m_x, m_y, m_w, m_h) state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color((m_x)*texpixel_size.x, (m_y)*texpixel_size.y, (m_w)*texpixel_size.x, (m_h)*texpixel_size.y))
+ Size2 texpixel_size;
- //top left
- DSTRECT(np->rect.position.x, np->rect.position.y, np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
- SRCRECT(np->source.position.x, np->source.position.y, np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- //top right
- DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y, np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
- SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y, np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ if (!texture) {
- //bottom right
- DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]);
- SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ texpixel_size = Size2(1, 1);
- //bottom left
- DSTRECT(np->rect.position.x, np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_LEFT], np->margin[MARGIN_BOTTOM]);
- SRCRECT(np->source.position.x, np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->margin[MARGIN_LEFT], np->margin[MARGIN_BOTTOM]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(0, 0, 1, 1));
- //top
- DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y, np->rect.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
- SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y, np->source.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ } else {
- //bottom
- DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y + np->rect.size.height - np->margin[MARGIN_BOTTOM], np->rect.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
- SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y + np->source.size.height - np->margin[MARGIN_BOTTOM], np->source.size.width - np->margin[MARGIN_LEFT] - np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ if (np->source != Rect2()) {
+ texpixel_size = Size2(1.0 / np->source.size.width, 1.0 / np->source.size.height);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(np->source.position.x / texture->width, np->source.position.y / texture->height, np->source.size.x / texture->width, np->source.size.y / texture->height));
+ } else {
+ texpixel_size = Size2(1.0 / texture->width, 1.0 / texture->height);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SRC_RECT, Color(0, 0, 1, 1));
+ }
+ }
- //left
- DSTRECT(np->rect.position.x, np->rect.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_LEFT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
- SRCRECT(np->source.position.x, np->source.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_LEFT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::CLIP_RECT_UV, false);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_REPEAT_H, int(np->axis_x));
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_REPEAT_V, int(np->axis_y));
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_DRAW_CENTER, np->draw_center);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::NP_MARGINS, Color(np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]));
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::DST_RECT, Color(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y));
- //right
- DSTRECT(np->rect.position.x + np->rect.size.width - np->margin[MARGIN_RIGHT], np->rect.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
- SRCRECT(np->source.position.x + np->source.size.width - np->margin[MARGIN_RIGHT], np->source.position.y + np->margin[MARGIN_TOP], np->margin[MARGIN_RIGHT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- if (np->draw_center) {
-
- //center
- DSTRECT(np->rect.position.x + np->margin[MARGIN_LEFT], np->rect.position.y + np->margin[MARGIN_TOP], np->rect.size.x - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->rect.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
- SRCRECT(np->source.position.x + np->margin[MARGIN_LEFT], np->source.position.y + np->margin[MARGIN_TOP], np->source.size.x - np->margin[MARGIN_LEFT] - np->margin[MARGIN_RIGHT], np->source.size.height - np->margin[MARGIN_TOP] - np->margin[MARGIN_BOTTOM]);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
-
-#undef SRCRECT
-#undef DSTRECT
-
storage->frame.canvas_draw_commands++;
} break;
@@ -608,6 +576,133 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
_draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1);
} break;
+ case Item::Command::TYPE_PARTICLES: {
+
+ Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c);
+
+ RasterizerStorageGLES3::Particles *particles = storage->particles_owner.getornull(particles_cmd->particles);
+ if (!particles)
+ break;
+
+ glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); //not used, so keep white
+
+ VisualServerRaster::redraw_request();
+
+ storage->particles_request_process(particles_cmd->particles);
+ //enable instancing
+
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, true);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, true);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, true);
+ //reset shader and force rebind
+ state.using_texture_rect = true;
+ _set_texture_rect_mode(false);
+
+ RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(particles_cmd->texture, particles_cmd->normal_map);
+
+ if (texture) {
+ Size2 texpixel_size(1.0 / (texture->width / particles_cmd->h_frames), 1.0 / (texture->height / particles_cmd->v_frames));
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ } else {
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, Vector2(1.0, 1.0));
+ }
+
+ if (!particles->use_local_coords) {
+
+ Transform2D inv_xf;
+ inv_xf.set_axis(0, Vector2(particles->emission_transform.basis.get_axis(0).x, particles->emission_transform.basis.get_axis(0).y));
+ inv_xf.set_axis(1, Vector2(particles->emission_transform.basis.get_axis(1).x, particles->emission_transform.basis.get_axis(1).y));
+ inv_xf.set_origin(Vector2(particles->emission_transform.get_origin().x, particles->emission_transform.get_origin().y));
+ inv_xf.affine_invert();
+
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform * inv_xf);
+ }
+
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::H_FRAMES, particles_cmd->h_frames);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::V_FRAMES, particles_cmd->v_frames);
+
+ glBindVertexArray(data.particle_quad_array); //use particle quad array
+ glBindBuffer(GL_ARRAY_BUFFER, particles->particle_buffers[0]); //bind particle buffer
+
+ int stride = sizeof(float) * 4 * 6;
+
+ int amount = particles->amount;
+
+ if (particles->draw_order != VS::PARTICLES_DRAW_ORDER_LIFETIME) {
+
+ glEnableVertexAttribArray(8); //xform x
+ glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3);
+ glVertexAttribDivisor(8, 1);
+ glEnableVertexAttribArray(9); //xform y
+ glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4);
+ glVertexAttribDivisor(9, 1);
+ glEnableVertexAttribArray(10); //xform z
+ glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5);
+ glVertexAttribDivisor(10, 1);
+ glEnableVertexAttribArray(11); //color
+ glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
+ glVertexAttribDivisor(11, 1);
+ glEnableVertexAttribArray(12); //custom
+ glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
+ glVertexAttribDivisor(12, 1);
+
+ glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount);
+ } else {
+ //split
+
+ int stride = sizeof(float) * 4 * 6;
+ int split = int(Math::ceil(particles->phase * particles->amount));
+
+ if (amount - split > 0) {
+ glEnableVertexAttribArray(8); //xform x
+ glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 3);
+ glVertexAttribDivisor(8, 1);
+ glEnableVertexAttribArray(9); //xform y
+ glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 4);
+ glVertexAttribDivisor(9, 1);
+ glEnableVertexAttribArray(10); //xform z
+ glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 5);
+ glVertexAttribDivisor(10, 1);
+ glEnableVertexAttribArray(11); //color
+ glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + 0);
+ glVertexAttribDivisor(11, 1);
+ glEnableVertexAttribArray(12); //custom
+ glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + stride * split + sizeof(float) * 4 * 2);
+ glVertexAttribDivisor(12, 1);
+
+ glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, amount - split);
+ }
+
+ if (split > 0) {
+ glEnableVertexAttribArray(8); //xform x
+ glVertexAttribPointer(8, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 3);
+ glVertexAttribDivisor(8, 1);
+ glEnableVertexAttribArray(9); //xform y
+ glVertexAttribPointer(9, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 4);
+ glVertexAttribDivisor(9, 1);
+ glEnableVertexAttribArray(10); //xform z
+ glVertexAttribPointer(10, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 5);
+ glVertexAttribDivisor(10, 1);
+ glEnableVertexAttribArray(11); //color
+ glVertexAttribPointer(11, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + 0);
+ glVertexAttribDivisor(11, 1);
+ glEnableVertexAttribArray(12); //custom
+ glVertexAttribPointer(12, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)NULL) + sizeof(float) * 4 * 2);
+ glVertexAttribDivisor(12, 1);
+
+ glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, split);
+ }
+ }
+
+ glBindVertexArray(0);
+
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCE_CUSTOM, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_INSTANCING, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_PARTICLES, false);
+ state.using_texture_rect = true;
+ _set_texture_rect_mode(false);
+
+ } break;
case Item::Command::TYPE_CIRCLE: {
_set_texture_rect_mode(false);
@@ -1351,7 +1446,39 @@ void RasterizerCanvasGLES3::initialize() {
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
}
+ {
+ //particle quad buffers
+
+ glGenBuffers(1, &data.particle_quad_vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
+ {
+ //quad of size 1, with pivot on the center for particles, then regular UVS. Color is general plus fetched from particle
+ const float qv[16] = {
+ -0.5, -0.5,
+ 0.0, 0.0,
+ -0.5, 0.5,
+ 0.0, 1.0,
+ 0.5, 0.5,
+ 1.0, 1.0,
+ 0.5, -0.5,
+ 1.0, 0.0
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 16, qv, GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ glGenVertexArrays(1, &data.particle_quad_array);
+ glBindVertexArray(data.particle_quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, data.particle_quad_vertices);
+ glEnableVertexAttribArray(VS::ARRAY_VERTEX);
+ glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, 0);
+ glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+ glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (float *)0 + 2);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
{
uint32_t poly_size = GLOBAL_DEF("rendering/buffers/canvas_polygon_buffer_size_kb", 128);
@@ -1428,6 +1555,9 @@ void RasterizerCanvasGLES3::finalize() {
glDeleteBuffers(1, &data.canvas_quad_vertices);
glDeleteVertexArrays(1, &data.canvas_quad_array);
+ glDeleteBuffers(1, &data.canvas_quad_vertices);
+ glDeleteVertexArrays(1, &data.canvas_quad_array);
+
glDeleteVertexArrays(1, &data.polygon_buffer_pointer_array);
}
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 4996f0a6b2..5f435275df 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -51,6 +51,10 @@ public:
GLuint polygon_buffer_quad_arrays[4];
GLuint polygon_buffer_pointer_array;
GLuint polygon_index_buffer;
+
+ GLuint particle_quad_vertices;
+ GLuint particle_quad_array;
+
uint32_t polygon_buffer_size;
} data;
@@ -63,6 +67,7 @@ public:
CanvasShadowShaderGLES3 canvas_shadow_shader;
bool using_texture_rect;
+ bool using_ninepatch;
RID current_tex;
RID current_normal;
@@ -107,7 +112,7 @@ public:
virtual void canvas_begin();
virtual void canvas_end();
- _FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable);
+ _FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false);
_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 913aa62452..96c3da99f0 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -977,6 +977,27 @@ void RasterizerSceneGLES3::environment_set_fog_height(RID p_env, bool p_enable,
env->fog_height_curve = p_height_curve;
}
+bool RasterizerSceneGLES3::is_environment(RID p_env) {
+
+ return environment_owner.owns(p_env);
+}
+
+VS::EnvironmentBG RasterizerSceneGLES3::environment_get_background(RID p_env) {
+
+ const Environment *env = environment_owner.getornull(p_env);
+ ERR_FAIL_COND_V(!env, VS::ENV_BG_MAX);
+
+ return env->bg_mode;
+}
+
+int RasterizerSceneGLES3::environment_get_canvas_max_layer(RID p_env) {
+
+ const Environment *env = environment_owner.getornull(p_env);
+ ERR_FAIL_COND_V(!env, -1);
+
+ return env->canvas_max_layer;
+}
+
RID RasterizerSceneGLES3::light_instance_create(RID p_light) {
LightInstance *light_instance = memnew(LightInstance);
@@ -3561,7 +3582,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p
glUniform2iv(state.exposure_shader.get_uniform(ExposureShaderGLES3::SOURCE_RENDER_SIZE), 1, ss);
glUniform2iv(state.exposure_shader.get_uniform(ExposureShaderGLES3::TARGET_SIZE), 1, ds);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->buffers.diffuse);
+ glBindTexture(GL_TEXTURE_2D, composite_from);
glBindFramebuffer(GL_FRAMEBUFFER, exposure_shrink[0].fbo);
glViewport(0, 0, exposure_shrink_size, exposure_shrink_size);
@@ -3957,6 +3978,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
use_mrt = use_mrt && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT];
use_mrt = use_mrt && !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_NO_3D_EFFECTS];
use_mrt = use_mrt && state.debug_draw != VS::VIEWPORT_DEBUG_DRAW_OVERDRAW;
+ use_mrt = use_mrt && env && (env->bg_mode != VS::ENV_BG_KEEP && env->bg_mode != VS::ENV_BG_CANVAS);
glViewport(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height);
@@ -4020,6 +4042,10 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->frame.clear_request = false;
}
+ } else if (env->bg_mode == VS::ENV_BG_CANVAS) {
+
+ clear_color = env->bg_color.to_linear();
+ storage->frame.clear_request = false;
} else if (env->bg_mode == VS::ENV_BG_COLOR) {
clear_color = env->bg_color.to_linear();
@@ -4037,7 +4063,39 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const
storage->frame.clear_request = false;
}
- glClearBufferfv(GL_COLOR, 0, clear_color.components); // specular
+ if (!env || env->bg_mode != VS::ENV_BG_KEEP) {
+ glClearBufferfv(GL_COLOR, 0, clear_color.components); // specular
+ }
+
+ if (env && env->bg_mode == VS::ENV_BG_CANVAS) {
+ //copy canvas to 3d buffer and convert it to linear
+
+ glDisable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, storage->frame.current_rt->color);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, true);
+
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SRGB_TO_LINEAR, true);
+
+ storage->shaders.copy.bind();
+
+ _copy_screen();
+
+ //turn off everything used
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::SRGB_TO_LINEAR, false);
+ storage->shaders.copy.set_conditional(CopyShaderGLES3::DISABLE_ALPHA, false);
+
+ //restore
+ glEnable(GL_BLEND);
+ glDepthMask(GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ }
state.texscreen_copied = false;
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 3e15da52ab..c52a00bf17 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -536,6 +536,11 @@ public:
virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve);
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve);
+ virtual bool is_environment(RID p_env);
+
+ virtual VS::EnvironmentBG environment_get_background(RID p_env);
+ virtual int environment_get_canvas_max_layer(RID p_env);
+
/* LIGHT INSTANCE */
struct LightDataUBO {
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 85331342e9..f7e1fdee9d 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -2326,6 +2326,9 @@ void RasterizerStorageGLES3::_update_material(Material *material) {
if (E->get().order < 0)
continue; // texture, does not go here
+ //if (material->shader->mode == VS::SHADER_PARTICLES) {
+ // print_line("uniform " + String(E->key()) + " order " + itos(E->get().order) + " offset " + itos(material->shader->ubo_offsets[E->get().order]));
+ //}
//regular uniform
uint8_t *data = &local_ubo[material->shader->ubo_offsets[E->get().order]];
@@ -5285,6 +5288,7 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
if (particles->clear) {
particles->cycle_number = 0;
+ particles->random_seed = Math::rand();
} else if (new_phase < particles->phase) {
particles->cycle_number++;
}
@@ -5295,6 +5299,8 @@ void RasterizerStorageGLES3::_particles_process(Particles *particles, float p_de
shaders.particles.set_uniform(ParticlesShaderGLES3::DELTA, p_delta * particles->speed_scale);
shaders.particles.set_uniform(ParticlesShaderGLES3::CLEAR, particles->clear);
+ glUniform1ui(shaders.particles.get_uniform_location(ParticlesShaderGLES3::RANDOM_SEED), particles->random_seed);
+
if (particles->use_local_coords)
shaders.particles.set_uniform(ParticlesShaderGLES3::EMISSION_TRANSFORM, Transform());
else
@@ -5350,6 +5356,33 @@ void RasterizerStorageGLES3::update_particles() {
Particles *particles = particle_update_list.first()->self();
+ if (particles->inactive && !particles->emitting) {
+
+ particle_update_list.remove(particle_update_list.first());
+ continue;
+ }
+
+ if (particles->emitting) {
+ if (particles->inactive) {
+ //restart system from scratch
+ particles->prev_ticks = 0;
+ particles->phase = 0;
+ particles->prev_phase = 0;
+ particles->clear = true;
+ particles->particle_valid_histories[0] = false;
+ particles->particle_valid_histories[1] = false;
+ }
+ particles->inactive = false;
+ particles->inactive_time = 0;
+ } else {
+ particles->inactive_time += particles->speed_scale * frame.delta;
+ if (particles->inactive_time > particles->lifetime * 1.2) {
+ particles->inactive = true;
+ particle_update_list.remove(particle_update_list.first());
+ continue;
+ }
+ }
+
Material *material = material_owner.getornull(particles->process_material);
if (!material || !material->shader || material->shader->mode != VS::SHADER_PARTICLES) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index ca0194bd5e..65026a16ec 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -1033,6 +1033,8 @@ public:
struct Particles : public GeometryOwner {
+ bool inactive;
+ float inactive_time;
bool emitting;
int amount;
float lifetime;
@@ -1060,6 +1062,7 @@ public:
float phase;
float prev_phase;
uint64_t prev_ticks;
+ uint32_t random_seed;
uint32_t cycle_number;
@@ -1088,6 +1091,7 @@ public:
frame_remainder = 0;
histories_enabled = false;
speed_scale = 1.0;
+ random_seed = 0;
custom_aabb = Rect3(Vector3(-4, -4, -4), Vector3(8, 8, 8));
@@ -1098,6 +1102,8 @@ public:
prev_ticks = 0;
clear = true;
+ inactive = true;
+ inactive_time = false;
glGenBuffers(2, particle_buffers);
glGenVertexArrays(2, particle_vaos);
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index 3376f99112..41421a3e2f 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -795,6 +795,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_PARTICLES].renames["INDEX"] = "index";
actions[VS::SHADER_PARTICLES].renames["GRAVITY"] = "current_gravity";
actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform";
+ actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed";
actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 8c6d15c3b7..e08ef0ad12 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -361,6 +361,8 @@ ShaderGLES3::Version *ShaderGLES3::get_current_version() {
ERR_FAIL_V(NULL);
}
+ //_display_error_with_code("pepo", strings);
+
/* FRAGMENT SHADER */
strings.resize(strings_base_size);
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 7be6ee857a..e97ce62daa 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -11,11 +11,26 @@ uniform vec4 src_rect;
#else
+#ifdef USE_INSTANCING
+
+layout(location=8) in highp vec4 instance_xform0;
+layout(location=9) in highp vec4 instance_xform1;
+layout(location=10) in highp vec4 instance_xform2;
+layout(location=11) in lowp vec4 instance_color;
+
+#ifdef USE_INSTANCE_CUSTOM
+layout(location=12) in highp vec4 instance_custom_data;
+#endif
+
+#endif
+
layout(location=4) in highp vec2 uv_attrib;
//skeletn
#endif
+uniform highp vec2 color_texpixel_size;
+
layout(std140) uniform CanvasItemData { //ubo:0
@@ -30,6 +45,12 @@ uniform highp mat4 extra_matrix;
out mediump vec2 uv_interp;
out mediump vec4 color_interp;
+#ifdef USE_NINEPATCH
+
+out highp vec2 pixel_size_interp;
+#endif
+
+
#ifdef USE_LIGHTING
layout(std140) uniform LightData { //ubo:1
@@ -64,7 +85,10 @@ const bool at_light_pass = true;
const bool at_light_pass = false;
#endif
-
+#ifdef USE_PARTICLES
+uniform int h_frames;
+uniform int v_frames;
+#endif
VERTEX_SHADER_GLOBALS
@@ -82,6 +106,12 @@ void main() {
vec4 vertex_color = color_attrib;
+#ifdef USE_INSTANCING
+ mat4 extra_matrix2 = extra_matrix * transpose(mat4(instance_xform0,instance_xform1,instance_xform2,vec4(0.0,0.0,0.0,1.0)));
+ vertex_color*=instance_color;
+#else
+ mat4 extra_matrix2 = extra_matrix;
+#endif
#ifdef USE_TEXTURE_RECT
@@ -95,6 +125,22 @@ void main() {
#endif
+#ifdef USE_PARTICLES
+ //scale by texture size
+ outvec.xy/=color_texpixel_size;
+
+ //compute h and v frames and adjust UV interp for animation
+ int total_frames = h_frames * v_frames;
+ int frame = min(int(float(total_frames) *instance_custom_data.z),total_frames-1);
+ float frame_w = 1.0/float(h_frames);
+ float frame_h = 1.0/float(v_frames);
+ uv_interp.x = uv_interp.x * frame_w + frame_w * float(frame % h_frames);
+ uv_interp.y = uv_interp.y * frame_h + frame_h * float(frame / v_frames);
+
+#endif
+
+#define extra_matrix extra_matrix2
+
{
vec2 src_vtx=outvec.xy;
@@ -102,11 +148,19 @@ VERTEX_SHADER_CODE
}
+
+#ifdef USE_NINEPATCH
+
+ pixel_size_interp=abs(dst_rect.zw) * vertex;
+#endif
+
#if !defined(SKIP_TRANSFORM_USED)
outvec = extra_matrix * outvec;
outvec = modelview_matrix * outvec;
#endif
+#undef extra_matrix
+
color_interp = vertex_color;
#ifdef USE_PIXEL_SNAP
@@ -235,6 +289,58 @@ uniform vec4 dst_rect;
uniform vec4 src_rect;
uniform bool clip_rect_uv;
+#ifdef USE_NINEPATCH
+
+in highp vec2 pixel_size_interp;
+
+uniform int np_repeat_v;
+uniform int np_repeat_h;
+uniform bool np_draw_center;
+//left top right bottom in pixel coordinates
+uniform vec4 np_margins;
+
+
+
+float map_ninepatch_axis(float pixel, float draw_size,float tex_pixel_size,float margin_begin,float margin_end,int np_repeat,inout int draw_center) {
+
+
+ float tex_size = 1.0/tex_pixel_size;
+
+ if (pixel < margin_begin) {
+ return pixel * tex_pixel_size;
+ } else if (pixel >= draw_size-margin_end) {
+ return (tex_size-(draw_size-pixel)) * tex_pixel_size;
+ } else {
+ if (!np_draw_center){
+ draw_center--;
+ }
+
+ if (np_repeat==0) { //stretch
+ //convert to ratio
+ float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
+ //scale to source texture
+ return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
+ } else if (np_repeat==1) { //tile
+ //convert to ratio
+ float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
+ //scale to source texture
+ return (margin_begin + ofs) * tex_pixel_size;
+ } else if (np_repeat==2) { //tile fit
+ //convert to ratio
+ float src_area = draw_size - margin_begin - margin_end;
+ float dst_area = tex_size - margin_begin - margin_end;
+ float scale = max(1.0,floor(src_area / max(dst_area,0.0000001) + 0.5));
+
+ //convert to ratio
+ float ratio = (pixel - margin_begin) / src_area;
+ ratio = mod(ratio * scale,1.0);
+ return (margin_begin + ratio * dst_area) * tex_pixel_size;
+ }
+ }
+
+}
+
+#endif
#endif
uniform bool use_default_normal;
@@ -245,6 +351,22 @@ void main() {
vec2 uv = uv_interp;
#ifdef USE_TEXTURE_RECT
+
+#ifdef USE_NINEPATCH
+
+ int draw_center=2;
+ uv = vec2(
+ map_ninepatch_axis(pixel_size_interp.x,abs(dst_rect.z),color_texpixel_size.x,np_margins.x,np_margins.z,np_repeat_h,draw_center),
+ map_ninepatch_axis(pixel_size_interp.y,abs(dst_rect.w),color_texpixel_size.y,np_margins.y,np_margins.w,np_repeat_v,draw_center)
+ );
+
+ if (draw_center==0) {
+ color.a=0;
+ }
+
+ uv = uv*src_rect.zw+src_rect.xy; //apply region if needed
+#endif
+
if (clip_rect_uv) {
vec2 half_texpixel = color_texpixel_size * 0.5;
@@ -267,6 +389,8 @@ void main() {
#endif
+
+
vec3 normal;
#if defined(NORMAL_USED)
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
index 4c8648903e..b0fb525e20 100644
--- a/drivers/gles3/shaders/copy.glsl
+++ b/drivers/gles3/shaders/copy.glsl
@@ -129,6 +129,11 @@ void main() {
color.rgb = mix( (vec3(1.0)+a)*pow(color.rgb,vec3(1.0/2.4))-a , 12.92*color.rgb , lessThan(color.rgb,vec3(0.0031308)));
#endif
+#ifdef SRGB_TO_LINEAR
+
+ color.rgb = mix(pow((color.rgb + vec3(0.055)) * (1.0 / (1 + 0.055)),vec3(2.4)),color.rgb * (1.0 / 12.92),lessThan(color.rgb,vec3(0.04045)));
+#endif
+
#ifdef DEBUG_GRADIENT
color.rg=uv_interp;
color.b=0.0;
diff --git a/drivers/gles3/shaders/particles.glsl b/drivers/gles3/shaders/particles.glsl
index 7e7b083f73..ec2577538c 100644
--- a/drivers/gles3/shaders/particles.glsl
+++ b/drivers/gles3/shaders/particles.glsl
@@ -37,6 +37,7 @@ uniform bool clear;
uniform uint cycle;
uniform float lifetime;
uniform mat4 emission_transform;
+uniform uint random_seed;
out highp vec4 out_color; //tfb:
@@ -104,7 +105,9 @@ void main() {
bool shader_active = velocity_active.a > 0.5;
if (system_phase > prev_system_phase) {
- if (prev_system_phase < restart_phase && system_phase >= restart_phase) {
+ // restart_phase >= prev_system_phase is used so particles emit in the first frame they are processed
+
+ if (restart_phase >= prev_system_phase && restart_phase < system_phase ) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (system_phase - restart_phase) * lifetime;
@@ -112,12 +115,12 @@ void main() {
}
} else {
- if (prev_system_phase < restart_phase) {
+ if (restart_phase >= prev_system_phase) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (1.0 - restart_phase + system_phase) * lifetime;
#endif
- } else if (system_phase >= restart_phase) {
+ } else if (restart_phase < system_phase ) {
restart=true;
#ifdef USE_FRACTIONAL_DELTA
local_delta = (system_phase - restart_phase) * lifetime;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 56b62cdf6e..2b29e4b08a 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -5311,36 +5311,6 @@ EditorNode::EditorNode() {
top_region->add_child(left_menu_hb);
menu_hb->add_child(top_region);
- PopupMenu *p;
-
- project_menu = memnew(MenuButton);
- project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools."));
- project_menu->set_text(TTR("Project"));
- project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
- left_menu_hb->add_child(project_menu);
-
- p = project_menu->get_popup();
- p->connect("id_pressed", this, "_menu_option");
- p->add_item(TTR("Run Script"), FILE_RUN_SCRIPT, KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_R);
- p->add_item(TTR("Export"), FILE_EXPORT_PROJECT);
-
- PopupMenu *tool_menu = memnew(PopupMenu);
- tool_menu->set_name("Tools");
- tool_menu->connect("id_pressed", this, "_menu_option");
- p->add_child(tool_menu);
- p->add_submenu_item(TTR("Tools"), "Tools");
- tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES);
- p->add_separator();
- p->add_item(TTR("Project Settings"), RUN_SETTINGS);
- p->add_separator();
-
-#ifdef OSX_ENABLED
- p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
-#else
- p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Q);
-#endif
- p->add_item(TTR("Quit"), FILE_QUIT, KEY_MASK_CMD + KEY_Q);
-
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("Scene"));
//file_menu->set_icon(gui_base->get_icon("Save","EditorIcons"));
@@ -5360,6 +5330,7 @@ EditorNode::EditorNode() {
ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB);
ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB);
ED_SHORTCUT("editor/filter_files", TTR("Filter Files.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_P);
+ PopupMenu *p;
file_menu->set_tooltip(TTR("Operations with scene files."));
p = file_menu->get_popup();
@@ -5379,7 +5350,6 @@ EditorNode::EditorNode() {
p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene.."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCRIPT);
p->add_separator();
-
PopupMenu *pm_export = memnew(PopupMenu);
pm_export->set_name("Export");
p->add_child(pm_export);
@@ -5405,6 +5375,35 @@ EditorNode::EditorNode() {
sp->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
menu_hb->add_child(sp);
}
+ p->add_separator();
+ p->add_item(TTR("Quit"), FILE_QUIT, KEY_MASK_CMD + KEY_Q);
+
+ project_menu = memnew(MenuButton);
+ project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools."));
+ project_menu->set_text(TTR("Project"));
+ project_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles"));
+ left_menu_hb->add_child(project_menu);
+
+ p = project_menu->get_popup();
+ p->add_item(TTR("Project Settings"), RUN_SETTINGS);
+ p->add_separator();
+ p->connect("id_pressed", this, "_menu_option");
+ p->add_item(TTR("Run Script"), FILE_RUN_SCRIPT, KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_R);
+ p->add_item(TTR("Export"), FILE_EXPORT_PROJECT);
+
+ PopupMenu *tool_menu = memnew(PopupMenu);
+ tool_menu->set_name("Tools");
+ tool_menu->connect("id_pressed", this, "_menu_option");
+ p->add_child(tool_menu);
+ p->add_submenu_item(TTR("Tools"), "Tools");
+ tool_menu->add_item(TTR("Orphan Resource Explorer"), TOOLS_ORPHAN_RESOURCES);
+ p->add_separator();
+
+#ifdef OSX_ENABLED
+ p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_ALT + KEY_Q);
+#else
+ p->add_item(TTR("Quit to Project List"), RUN_PROJECT_MANAGER, KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_Q);
+#endif
PanelContainer *editor_region = memnew(PanelContainer);
main_editor_button_vb = memnew(HBoxContainer);
@@ -6115,7 +6114,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(GradientEditorPlugin(this)));
add_editor_plugin(memnew(GradientTextureEditorPlugin(this)));
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
- add_editor_plugin(memnew(CurveTextureEditorPlugin(this)));
+ add_editor_plugin(memnew(CurveEditorPlugin(this)));
add_editor_plugin(memnew(TextureEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
//add_editor_plugin( memnew( MaterialEditorPlugin(this) ) );
diff --git a/editor/icons/icon_audio_player.png b/editor/icons/icon_audio_player.png
deleted file mode 100644
index c3e6d6cafa..0000000000
--- a/editor/icons/icon_audio_player.png
+++ /dev/null
Binary files differ
diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp
index 6dee5da538..4ebbcb1610 100644
--- a/editor/import/editor_import_plugin.cpp
+++ b/editor/import/editor_import_plugin.cpp
@@ -56,7 +56,7 @@ String EditorImportPlugin::get_preset_name(int p_idx) const {
return get_script_instance()->call("get_preset_name", p_idx);
}
-int EditorImportPlugin::get_preset_count() {
+int EditorImportPlugin::get_preset_count() const {
ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_preset_count")), 0);
return get_script_instance()->call("get_preset_count");
}
diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h
index 3c16b79713..b60813db61 100644
--- a/editor/import/editor_import_plugin.h
+++ b/editor/import/editor_import_plugin.h
@@ -43,7 +43,7 @@ public:
virtual String get_visible_name() const;
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual String get_preset_name(int p_idx) const;
- virtual int get_preset_count();
+ virtual int get_preset_count() const;
virtual String get_save_extension() const;
virtual String get_resource_type() const;
virtual void get_import_options(List<ImportOption> *r_options, int p_preset) const;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 803e59469c..4313c1a91b 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -3270,6 +3270,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
PopupMenu *p;
p = edit_menu->get_popup();
+ p->set_hide_on_checkable_item_selection(false);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_snap", TTR("Use Snap")), SNAP_USE);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Show Grid")), SNAP_SHOW_GRID);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION);
@@ -3291,6 +3292,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
skeleton_menu->add_separator();
skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN);
skeleton_menu->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN);
+ skeleton_menu->set_hide_on_checkable_item_selection(false);
skeleton_menu->connect("id_pressed", this, "_popup_callback");
/*
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index d869d703f1..50a625ddc1 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -27,528 +27,695 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "curve_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
+#include "core_string_names.h"
+#include "os/input.h"
#include "os/keyboard.h"
-#include "spatial_editor_plugin.h"
-void CurveTextureEdit::_gui_input(const Ref<InputEvent> &p_event) {
+CurveEditor::CurveEditor() {
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
+ _hover_radius = 6;
+ _tangents_length = 40;
+ _dragging = false;
+ _has_undo_data = false;
+ _world_rect = Rect2(0, 0, 1, 1);
- Ref<InputEventKey> k = p_event;
- if (k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && grabbed != -1) {
+ set_focus_mode(FOCUS_ALL);
+ set_clip_contents(true);
+
+ _context_menu = memnew(PopupMenu);
+ _context_menu->connect("id_pressed", this, "_on_context_menu_item_selected");
+ add_child(_context_menu);
+
+ _presets_menu = memnew(PopupMenu);
+ _presets_menu->set_name("_presets_menu");
+ _presets_menu->add_item("Flat0", PRESET_FLAT0);
+ _presets_menu->add_item("Flat1", PRESET_FLAT1);
+ _presets_menu->add_item("Linear", PRESET_LINEAR);
+ _presets_menu->add_item("Ease in", PRESET_EASE_IN);
+ _presets_menu->add_item("Ease out", PRESET_EASE_OUT);
+ _presets_menu->add_item("Smoothstep", PRESET_SMOOTHSTEP);
+ _presets_menu->connect("id_pressed", this, "_on_preset_item_selected");
+ _context_menu->add_child(_presets_menu);
+}
- points.remove(grabbed);
- grabbed = -1;
- update();
- emit_signal("curve_changed");
- accept_event();
+void CurveEditor::set_curve(Ref<Curve> curve) {
+
+ if (curve == _curve_ref)
+ return;
+
+ if (_curve_ref.is_valid()) {
+ _curve_ref->disconnect("changed", this, "_curve_changed");
+ }
+ _curve_ref = curve;
+ if (_curve_ref.is_valid()) {
+ _curve_ref->connect("changed", this, "_curve_changed");
}
- Ref<InputEventMouseButton> mb = p_event;
+ _selected_point = -1;
+ _hover_point = -1;
+ _selected_tangent = TANGENT_NONE;
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ update();
- update();
- Ref<Font> font = get_font("font", "Label");
+ // Note: if you edit a curve, then set another, and try to undo,
+ // it will normally apply on the previous curve, but you won't see it
+}
+
+Size2 CurveEditor::get_minimum_size() const {
+ return Vector2(64, 64);
+}
+
+void CurveEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW)
+ _draw();
+}
+
+void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb_ref = p_event;
+ if (mb_ref.is_valid()) {
+
+ const InputEventMouseButton &mb = **mb_ref;
- int font_h = font->get_height();
+ if (mb.is_pressed() && !_dragging) {
- Vector2 size = get_size();
- size.y -= font_h;
+ Vector2 mpos = mb.get_position();
- Point2 p = Vector2(mb->get_position().x, mb->get_position().y) / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- grabbed = -1;
- grabbing = true;
+ _selected_tangent = get_tangent_at(mpos);
+ if (_selected_tangent == TANGENT_NONE)
+ set_selected_point(get_point_at(mpos));
- for (int i = 0; i < points.size(); i++) {
+ switch (mb.get_button_index()) {
+ case BUTTON_RIGHT:
+ _context_click_pos = mpos;
+ open_context_menu(get_global_transform().xform(mpos));
+ break;
- Vector2 ps = p * get_size();
- Vector2 pt = Vector2(points[i].offset, points[i].height) * get_size();
- if (ps.distance_to(pt) < 4) {
- grabbed = i;
+ case BUTTON_MIDDLE:
+ remove_point(_hover_point);
+ break;
+
+ case BUTTON_LEFT:
+ _dragging = true;
+ break;
}
}
- //grab or select
- if (grabbed != -1) {
- return;
- }
- //insert
-
- Point np;
- np.offset = p.x;
- np.height = p.y;
-
- points.push_back(np);
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ if (!mb.is_pressed() && _dragging && mb.get_button_index() == BUTTON_LEFT) {
+ _dragging = false;
+ if (_has_undo_data) {
+ push_undo(_undo_data);
+ _has_undo_data = false;
}
}
-
- emit_signal("curve_changed");
}
- if (mb.is_valid() && mb->get_button_index() == 1 && !mb->is_pressed()) {
+ Ref<InputEventMouseMotion> mm_ref = p_event;
+ if (mm_ref.is_valid()) {
- if (grabbing) {
- grabbing = false;
- emit_signal("curve_changed");
- }
- update();
- }
+ const InputEventMouseMotion &mm = **mm_ref;
- Ref<InputEventMouseMotion> mm = p_event;
+ Vector2 mpos = mm.get_position();
- if (mm.is_valid() && grabbing && grabbed != -1) {
+ if (_dragging && _curve_ref.is_valid()) {
+ if (_selected_point != -1) {
- Ref<Font> font = get_font("font", "Label");
- int font_h = font->get_height();
- Vector2 size = get_size();
- size.y -= font_h;
+ if (!_has_undo_data) {
+ // Save curve state before dragging points
+ _undo_data = _curve_ref->get_data();
+ _has_undo_data = true;
+ }
- Point2 p = mm->get_position() / size;
- p.y = CLAMP(1.0 - p.y, 0, 1) * (max - min) + min;
- p.x = CLAMP(p.x, 0.0, 1.0);
+ if (_selected_tangent == TANGENT_NONE) {
+ // Drag point
- bool valid = true;
+ Vector2 point_pos = get_world_pos(mpos);
- for (int i = 0; i < points.size(); i++) {
+ int i = _curve_ref->set_point_offset(_selected_point, point_pos.x);
+ // The index may change if the point is dragged across another one
+ set_hover_point_index(i);
+ set_selected_point(i);
- if (points[i].offset == p.x && points[i].height == p.y && i != grabbed) {
- valid = false;
- }
- }
+ // TODO Get rid of this clamp if zoom is implemented in this editor.
+ // This is to prevent the user from loosing a point out of view.
+ if (point_pos.y < 0.0)
+ point_pos.y = 0.0;
+ else if (point_pos.y > 1.0)
+ point_pos.y = 1.0;
+
+ _curve_ref->set_point_value(_selected_point, point_pos.y);
- if (!valid)
- return;
+ //auto_calculate_tangents(i);
- points[grabbed].offset = p.x;
- points[grabbed].height = p.y;
+ } else {
+ // Drag tangent
- points.sort();
- for (int i = 0; i < points.size(); i++) {
- if (points[i].offset == p.x && points[i].height == p.y) {
- grabbed = i;
- break;
+ Vector2 point_pos = _curve_ref->get_point_pos(_selected_point);
+ Vector2 control_pos = get_world_pos(mpos);
+
+ Vector2 dir = (control_pos - point_pos).normalized();
+
+ real_t tangent;
+ if (Math::abs(dir.x) > CMP_EPSILON)
+ tangent = dir.y / dir.x;
+ else
+ tangent = 9999 * (dir.y >= 0 ? 1 : -1);
+
+ bool link = !Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+
+ if (_selected_tangent == TANGENT_LEFT) {
+ _curve_ref->set_point_left_tangent(_selected_point, tangent);
+ if (link && _selected_point != _curve_ref->get_point_count() - 1)
+ _curve_ref->set_point_right_tangent(_selected_point, tangent);
+ } else {
+ _curve_ref->set_point_right_tangent(_selected_point, tangent);
+ if (link && _selected_point != 0)
+ _curve_ref->set_point_left_tangent(_selected_point, tangent);
+ }
+ }
}
+
+ } else {
+ set_hover_point_index(get_point_at(mpos));
}
+ }
- emit_signal("curve_changed");
+ Ref<InputEventKey> key_ref = p_event;
+ if (key_ref.is_valid()) {
+ const InputEventKey &key = **key_ref;
- update();
+ if (key.is_pressed() && _selected_point != -1) {
+ if (key.get_scancode() == KEY_DELETE)
+ remove_point(_selected_point);
+ }
}
}
-void CurveTextureEdit::_plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d) {
+void CurveEditor::on_preset_item_selected(int preset_id) {
+ ERR_FAIL_COND(preset_id < 0 || preset_id >= PRESET_COUNT);
+ ERR_FAIL_COND(_curve_ref.is_null());
- Ref<Font> font = get_font("font", "Label");
-
- int font_h = font->get_height();
+ Curve &curve = **_curve_ref;
+ Array previous_data = curve.get_data();
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx, lasty;
- int newx, newy;
- int ntimes;
- int i, j;
+ curve.clear_points();
- int xmax = get_size().x;
- int ymax = get_size().y - font_h;
+ switch (preset_id) {
+ case PRESET_FLAT0:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 0));
+ break;
- int vsplits = 4;
+ case PRESET_FLAT1:
+ curve.add_point(Vector2(0, 1));
+ curve.add_point(Vector2(1, 1));
+ break;
- int zero_ofs = (1.0 - (0.0 - min) / (max - min)) * ymax;
+ case PRESET_LINEAR:
+ curve.add_point(Vector2(0, 0), 0, 1);
+ curve.add_point(Vector2(1, 1), 1, 0);
+ break;
- draw_line(Vector2(0, zero_ofs), Vector2(xmax, zero_ofs), Color(0.8, 0.8, 0.8, 0.15), 2.0);
+ case PRESET_EASE_IN:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1), 1.4, 0);
+ break;
- for (int i = 0; i <= vsplits; i++) {
- float fofs = float(i) / vsplits;
- int yofs = fofs * ymax;
- draw_line(Vector2(xmax, yofs), Vector2(xmax - 4, yofs), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ case PRESET_EASE_OUT:
+ curve.add_point(Vector2(0, 0), 0, 1.4);
+ curve.add_point(Vector2(1, 1));
+ break;
- String text = rtos((1.0 - fofs) * (max - min) + min);
- int ppos = text.find(".");
- if (ppos != -1) {
- if (text.length() > ppos + 2)
- text = text.substr(0, ppos + 2);
- }
+ case PRESET_SMOOTHSTEP:
+ curve.add_point(Vector2(0, 0));
+ curve.add_point(Vector2(1, 1));
+ break;
- int size = font->get_string_size(text).x;
- int xofs = xmax - size - 4;
- yofs -= font_h / 2;
+ default:
+ break;
+ }
- if (yofs < 2) {
- yofs = 2;
- } else if (yofs + font_h > ymax - 2) {
- yofs = ymax - font_h - 2;
- }
+ push_undo(previous_data);
+}
- draw_string(font, Vector2(xofs, yofs + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
+void CurveEditor::_curve_changed() {
+ update();
+ // Point count can change in case of undo
+ if (_selected_point >= _curve_ref->get_point_count()) {
+ set_selected_point(-1);
}
+}
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveEditor::on_context_menu_item_selected(int action_id) {
+ switch (action_id) {
+ case CONTEXT_ADD_POINT:
+ add_point(_context_click_pos);
+ break;
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = ((p_a[1] - min) / (max - min) * ymax);
- geometry[1][1] = ((p_b[1] - min) / (max - min) * ymax);
- geometry[2][1] = ((p_c[1] - min) / (max - min) * ymax);
- geometry[3][1] = ((p_d[1] - min) / (max - min) * ymax);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ case CONTEXT_REMOVE_POINT:
+ remove_point(_selected_point);
+ break;
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
+}
+
+void CurveEditor::open_context_menu(Vector2 pos) {
+ _context_menu->set_position(pos);
+
+ _context_menu->clear();
+
+ if (_curve_ref.is_valid()) {
+ _context_menu->add_item(TTR("Add point"), CONTEXT_ADD_POINT);
+ if (_selected_point >= 0) {
+ _context_menu->add_item(TTR("Remove point"), CONTEXT_REMOVE_POINT);
}
+ _context_menu->add_separator();
}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
+ _context_menu->add_submenu_item(TTR("Load preset"), _presets_menu->get_name());
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
+ _context_menu->popup();
+}
- lastx = CLAMP(x, 0, xmax);
- lasty = CLAMP(y, 0, ymax);
+int CurveEditor::get_point_at(Vector2 pos) const {
+ if (_curve_ref.is_null())
+ return -1;
+ const Curve &curve = **_curve_ref;
- /* if (fix255)
- {
- cd->curve[cd->outline][lastx] = lasty;
- }
- else
- {
- cd->curve_ptr[cd->outline][lastx] = lasty;
- if(gb_debug) printf("bender_plot_curve xmax:%d ymax:%d\n", (int)xmax, (int)ymax);
+ const float r = _hover_radius * _hover_radius;
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 p = get_view_pos(curve.get_point_pos(i));
+ if (p.distance_squared_to(pos) <= r) {
+ return i;
}
-*/
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP((Math::round(y)), 0, ymax);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
-#if 0
- if(fix255)
- {
- /* use fixed array size (for the curve graph) */
- cd->curve[cd->outline][newx] = newy;
- }
- else
- {
- /* use dynamic allocated curve_ptr (for the real curve) */
- cd->curve_ptr[cd->outline][newx] = newy;
+ }
- if(gb_debug) printf("outline: %d cX: %d cY: %d\n", (int)cd->outline, (int)newx, (int)newy);
- }
-#endif
- draw_line(Vector2(lastx, ymax - lasty), Vector2(newx, ymax - newy), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ return -1;
+}
+
+int CurveEditor::get_tangent_at(Vector2 pos) const {
+ if (_curve_ref.is_null() || _selected_point < 0)
+ return TANGENT_NONE;
+
+ if (_selected_point != 0) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_LEFT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_LEFT;
}
+ }
- lastx = newx;
- lasty = newy;
+ if (_selected_point != _curve_ref->get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(_selected_point, TANGENT_RIGHT);
+ if (control_pos.distance_to(pos) < _hover_radius) {
+ return TANGENT_RIGHT;
+ }
}
- int splits = 8;
+ return TANGENT_NONE;
+}
- draw_line(Vector2(0, ymax - 1), Vector2(xmax, ymax - 1), Color(0.8, 0.8, 0.8, 0.3), 2.0);
+void CurveEditor::add_point(Vector2 pos) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- for (int i = 0; i <= splits; i++) {
- float fofs = float(i) / splits;
- draw_line(Vector2(fofs * xmax, ymax), Vector2(fofs * xmax, ymax - 2), Color(0.8, 0.8, 0.8, 0.8), 2.0);
+ Array prev_data = _curve_ref->get_data();
- String text = rtos(fofs);
- int size = font->get_string_size(text).x;
- int ofs = fofs * xmax - size * 0.5;
- if (ofs < 2) {
- ofs = 2;
- } else if (ofs + size > xmax - 2) {
- ofs = xmax - size - 2;
- }
+ Vector2 point_pos = get_world_pos(pos);
+ if (point_pos.y < 0.0)
+ point_pos.y = 0.0;
+ else if (point_pos.y > 1.0)
+ point_pos.y = 1.0;
- draw_string(font, Vector2(ofs, ymax + font->get_ascent()), text, Color(0.8, 0.8, 0.8, 1));
- }
+ _curve_ref->add_point(point_pos);
+
+ push_undo(prev_data);
}
-void CurveTextureEdit::_notification(int p_what) {
+void CurveEditor::remove_point(int index) {
+ ERR_FAIL_COND(_curve_ref.is_null());
- if (p_what == NOTIFICATION_DRAW) {
+ Array prev_data = _curve_ref->get_data();
- Ref<Font> font = get_font("font", "Label");
+ _curve_ref->remove_point(index);
- int font_h = font->get_height();
+ if (index == _selected_point)
+ set_selected_point(-1);
- draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), get_size()));
+ push_undo(prev_data);
+}
- int w = get_size().x;
- int h = get_size().y;
+void CurveEditor::set_selected_point(int index) {
+ if (index != _selected_point) {
+ _selected_point = index;
+ update();
+ }
+}
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
+void CurveEditor::set_hover_point_index(int index) {
+ if (index != _hover_point) {
+ _hover_point = index;
+ update();
+ }
+}
- for (int i = -1; i < points.size(); i++) {
+void CurveEditor::push_undo(Array previous_curve_data) {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= points.size()) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(points[i + 1].offset, points[i + 1].height);
- }
+ ur->create_action(TTR("Modify Curve"));
+ ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data());
+ ur->add_undo_method(*_curve_ref, "_set_data", previous_curve_data);
- if (i + 2 >= points.size()) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(points[i + 2].offset, points[i + 2].height);
- }
+ // This boolean is to prevent commit_action from executing the do method,
+ // because at this point it's already done, there is no point in doing it twice
+ _curve_ref->_disable_set_data = true;
+ ur->commit_action();
+ _curve_ref->_disable_set_data = false;
+}
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
+void CurveEditor::update_view_transform() {
+ Vector2 control_size = get_size();
+ const real_t margin = 24;
- _plot_curve(prev2, prev, next, next2);
+ _world_rect = Rect2(Curve::MIN_X, 0, Curve::MAX_X, 1);
+ Vector2 wm = Vector2(margin, margin) / control_size;
+ _world_rect.position -= wm;
+ _world_rect.size += 2.0 * wm;
- prev2 = prev;
- prev = next;
- }
+ _world_to_view = Transform2D();
+ _world_to_view.translate(-_world_rect.position - Vector2(0, _world_rect.size.y));
+ _world_to_view.scale(Vector2(control_size.x, -control_size.y) / _world_rect.size);
+}
- Vector2 size = get_size();
- size.y -= font_h;
- for (int i = 0; i < points.size(); i++) {
+Vector2 CurveEditor::get_tangent_view_pos(int i, TangentIndex tangent) const {
- Color col = i == grabbed ? Color(1, 0.0, 0.0, 0.9) : Color(1, 1, 1, 0.8);
+ Vector2 dir;
+ if (tangent == TANGENT_LEFT)
+ dir = -Vector2(1, _curve_ref->get_point_left_tangent(i));
+ else
+ dir = Vector2(1, _curve_ref->get_point_right_tangent(i));
- float h = (points[i].height - min) / (max - min);
- draw_rect(Rect2(Vector2(points[i].offset, 1.0 - h) * size - Vector2(2, 2), Vector2(5, 5)), col);
- }
+ Vector2 point_pos = get_view_pos(_curve_ref->get_point_pos(i));
+ Vector2 control_pos = get_view_pos(_curve_ref->get_point_pos(i) + dir);
- /* if (grabbed!=-1) {
+ return point_pos + _tangents_length * (control_pos - point_pos).normalized();
+}
- draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color);
- }
-*/
- if (has_focus()) {
+Vector2 CurveEditor::get_view_pos(Vector2 world_pos) const {
+ return _world_to_view.xform(world_pos);
+}
+
+Vector2 CurveEditor::get_world_pos(Vector2 view_pos) const {
+ return _world_to_view.affine_inverse().xform(view_pos);
+}
- draw_line(Vector2(-1, -1), Vector2(w + 1, -1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, -1), Vector2(w + 1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+// Uses non-baked points, but takes advantage of ordered iteration to be faster
+template <typename T>
+static void plot_curve_accurate(const Curve &curve, float step, T plot_func) {
+
+ if (curve.get_point_count() <= 1) {
+ // Not enough points to make a curve, so it's just a straight line
+ float y = curve.interpolate(0);
+ plot_func(Vector2(0, y), Vector2(1.f, y), true);
+
+ } else {
+ Vector2 first_point = curve.get_point_pos(0);
+ Vector2 last_point = curve.get_point_pos(curve.get_point_count() - 1);
+
+ // Edge lines
+ plot_func(Vector2(0, first_point.y), first_point, false);
+ plot_func(Vector2(Curve::MAX_X, last_point.y), last_point, false);
+
+ // Draw section by section, so that we get maximum precision near points.
+ // It's an accurate representation, but slower than using the baked one.
+ for (int i = 1; i < curve.get_point_count(); ++i) {
+ Vector2 a = curve.get_point_pos(i - 1);
+ Vector2 b = curve.get_point_pos(i);
+
+ Vector2 pos = a;
+ Vector2 prev_pos = a;
+
+ float len = b.x - a.x;
+ //float step = 4.f / view_size.x;
+
+ for (float x = step; x < len; x += step) {
+ pos.x = a.x + x;
+ pos.y = curve.interpolate_local_nocheck(i - 1, x);
+ plot_func(prev_pos, pos, true);
+ prev_pos = pos;
+ }
+
+ plot_func(prev_pos, b, true);
}
}
}
-Size2 CurveTextureEdit::get_minimum_size() const {
+struct CanvasItemPlotCurve {
- return Vector2(64, 64);
-}
+ CanvasItem &ci;
+ Color color1;
+ Color color2;
-void CurveTextureEdit::set_range(float p_min, float p_max) {
- max = p_max;
- min = p_min;
- update();
-}
+ CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2)
+ : ci(p_ci), color1(p_color1), color2(p_color2) {}
-void CurveTextureEdit::set_points(const Vector<Vector2> &p_points) {
+ void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
+ ci.draw_line(pos0, pos1, in_definition ? color1 : color2);
+ }
+};
+
+void CurveEditor::_draw() {
+ if (_curve_ref.is_null())
+ return;
+ Curve &curve = **_curve_ref;
+
+ update_view_transform();
+
+ // Background
+
+ Vector2 view_size = get_rect().size;
+ draw_style_box(get_stylebox("bg", "Tree"), Rect2(Point2(), view_size));
+
+ // Grid
+
+ draw_set_transform_matrix(_world_to_view);
+
+ Vector2 min_edge = get_world_pos(Vector2(0, view_size.y));
+ Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0));
+
+ const Color grid_color0(0, 0, 0, 0.5);
+ const Color grid_color1(0, 0, 0, 0.15);
+ draw_line(Vector2(min_edge.x, 0), Vector2(max_edge.x, 0), grid_color0);
+ draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0);
+ draw_line(Vector2(1, max_edge.y), Vector2(1, min_edge.y), grid_color0);
+ draw_line(Vector2(max_edge.x, 1), Vector2(min_edge.x, 1), grid_color0);
- points.clear();
- for (int i = 0; i < p_points.size(); i++) {
- Point p;
- p.offset = p_points[i].x;
- p.height = p_points[i].y;
- points.push_back(p);
+ const Vector2 grid_step(0.25, 0.5);
+
+ for (real_t x = 0; x < 1.0; x += grid_step.x) {
+ draw_line(Vector2(x, min_edge.y), Vector2(x, max_edge.y), grid_color1);
+ }
+ for (real_t y = 0; y < 1.0; y += grid_step.y) {
+ draw_line(Vector2(min_edge.x, y), Vector2(max_edge.x, y), grid_color1);
}
- points.sort();
- update();
-}
+ // Markings
-Vector<Vector2> CurveTextureEdit::get_points() const {
- Vector<Vector2> ret;
- for (int i = 0; i < points.size(); i++)
- ret.push_back(Vector2(points[i].offset, points[i].height));
- return ret;
-}
+ draw_set_transform_matrix(Transform2D());
-void CurveTextureEdit::_bind_methods() {
+ Ref<Font> font = get_font("font", "Label");
+ const Color text_color(1, 1, 1, 0.3);
- ClassDB::bind_method(D_METHOD("_gui_input"), &CurveTextureEdit::_gui_input);
+ draw_string(font, get_view_pos(Vector2(0, 0)), "0.0", text_color);
- ADD_SIGNAL(MethodInfo("curve_changed"));
-}
+ draw_string(font, get_view_pos(Vector2(0.25, 0)), "0.25", text_color);
+ draw_string(font, get_view_pos(Vector2(0.5, 0)), "0.5", text_color);
+ draw_string(font, get_view_pos(Vector2(0.75, 0)), "0.75", text_color);
+ draw_string(font, get_view_pos(Vector2(1, 0)), "1.0", text_color);
-CurveTextureEdit::CurveTextureEdit() {
+ draw_string(font, get_view_pos(Vector2(0, 0.5)), "0.5", text_color);
+ draw_string(font, get_view_pos(Vector2(0, 1)), "1.0", text_color);
- grabbed = -1;
- grabbing = false;
- max = 1;
- min = 0;
- set_focus_mode(FOCUS_ALL);
-}
+ // Draw tangents for current point
-void CurveTextureEditorPlugin::_curve_settings_changed() {
+ if (_selected_point >= 0) {
- if (!curve_texture_ref.is_valid())
- return;
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
-}
+ const Color tangent_color(0.5, 0.5, 1, 1);
+
+ int i = _selected_point;
+ Vector2 pos = curve.get_point_pos(i);
+
+ if (i != 0) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_LEFT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
-CurveTextureEditorPlugin::CurveTextureEditorPlugin(EditorNode *p_node) {
+ if (i != curve.get_point_count() - 1) {
+ Vector2 control_pos = get_tangent_view_pos(i, TANGENT_RIGHT);
+ draw_line(get_view_pos(pos), control_pos, tangent_color);
+ draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), tangent_color);
+ }
+ }
- editor = p_node;
- curve_editor = memnew(CurveTextureEdit);
+ // Draw lines
- curve_button = editor->add_bottom_panel_item("CurveTexture", curve_editor);
+ draw_set_transform_matrix(_world_to_view);
- curve_button->hide();
- curve_editor->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
- curve_editor->hide();
- curve_editor->connect("curve_changed", this, "curve_changed");
-}
+ const Color line_color(1, 1, 1, 0.85);
+ const Color edge_line_color(1, 1, 1, 0.4);
+
+ CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color);
+ plot_curve_accurate(curve, 4.f / view_size.x, plot_func);
+
+ /*// TEST draw baked curve
+ {
+ Vector2 pos = Vector2(0, curve.interpolate_baked(0));
+ Vector2 prev_pos = pos;
-void CurveTextureEditorPlugin::edit(Object *p_object) {
+ float len = 1.0;
+ float step = 4.f / view_size.x;
- if (curve_texture_ref.is_valid()) {
- curve_texture_ref->disconnect("changed", this, "_curve_settings_changed");
+ for(float x = step; x < len; x += step) {
+ pos.x = x;
+ pos.y = curve.interpolate_baked(x);
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(pos), Color(0,1,0));
+ prev_pos = pos;
+ }
+
+ draw_line(get_point_view_pos(prev_pos), get_point_view_pos(Vector2(1, curve.interpolate_baked(1))), Color(0,1,0));
+ }//*/
+
+ // Draw points
+
+ draw_set_transform_matrix(Transform2D());
+
+ const Color point_color(1, 1, 1);
+ const Color selected_point_color(1, 0.5, 0.5);
+
+ for (int i = 0; i < curve.get_point_count(); ++i) {
+ Vector2 pos = curve.get_point_pos(i);
+ draw_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(3), i == _selected_point ? selected_point_color : point_color);
+ // TODO Circles are prettier. Needs a fix! Or a texture
+ //draw_circle(pos, 2, point_color);
}
- CurveTexture *curve_texture = p_object->cast_to<CurveTexture>();
- if (!curve_texture)
- return;
- curve_texture_ref = Ref<CurveTexture>(curve_texture);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->set_range(curve_texture_ref->get_min(), curve_texture_ref->get_max());
- if (!curve_texture_ref->is_connected("changed", this, "_curve_settings_changed")) {
- curve_texture_ref->connect("changed", this, "_curve_settings_changed");
+
+ // Hover
+
+ if (_hover_point != -1) {
+ const Color hover_color = line_color;
+ Vector2 pos = curve.get_point_pos(_hover_point);
+ stroke_rect(Rect2(get_view_pos(pos), Vector2(1, 1)).grow(_hover_radius), hover_color);
}
}
-bool CurveTextureEditorPlugin::handles(Object *p_object) const {
+// TODO That should be part of the drawing API...
+void CurveEditor::stroke_rect(Rect2 rect, Color color) {
+
+ // a---b
+ // | |
+ // c---d
+ Vector2 a(rect.position);
+ Vector2 b(rect.position.x + rect.size.x, rect.position.y);
+ Vector2 c(rect.position.x, rect.position.y + rect.size.y);
+ Vector2 d(rect.position + rect.size);
+
+ draw_line(a, b, color);
+ draw_line(b, d, color);
+ draw_line(d, c, color);
+ draw_line(c, a, color);
+}
- return p_object->is_class("CurveTexture");
+void CurveEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_gui_input"), &CurveEditor::on_gui_input);
+ ClassDB::bind_method(D_METHOD("_on_preset_item_selected"), &CurveEditor::on_preset_item_selected);
+ ClassDB::bind_method(D_METHOD("_curve_changed"), &CurveEditor::_curve_changed);
+ ClassDB::bind_method(D_METHOD("_on_context_menu_item_selected"), &CurveEditor::on_context_menu_item_selected);
}
-void CurveTextureEditorPlugin::make_visible(bool p_visible) {
+//---------------
- if (p_visible) {
- curve_button->show();
- editor->make_bottom_panel_item_visible(curve_editor);
+CurveEditorPlugin::CurveEditorPlugin(EditorNode *p_node) {
+ _editor_node = p_node;
- } else {
+ _view = memnew(CurveEditor);
+ _view->set_custom_minimum_size(Size2(100, 128 * EDSCALE));
+ _view->hide();
- curve_button->hide();
- if (curve_editor->is_visible_in_tree())
- editor->hide_bottom_panel();
- }
+ _toggle_button = _editor_node->add_bottom_panel_item(get_name(), _view);
+ _toggle_button->hide();
}
-void CurveTextureEditorPlugin::_curve_changed() {
+CurveEditorPlugin::~CurveEditorPlugin() {
+}
- if (curve_texture_ref.is_valid()) {
+void CurveEditorPlugin::edit(Object *p_object) {
- UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ Ref<Curve> curve_ref;
- Vector<Vector2> points = curve_editor->get_points();
- PoolVector<Vector2> ppoints = Variant(points);
+ if (_current_ref.is_valid()) {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct)
+ ct->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ }
+
+ if (p_object) {
+ Resource *res = p_object->cast_to<Resource>();
+ ERR_FAIL_COND(res == NULL);
+ ERR_FAIL_COND(!handles(p_object));
+
+ _current_ref = Ref<Resource>(p_object->cast_to<Resource>());
+
+ if (_current_ref.is_valid()) {
+ Curve *curve = _current_ref->cast_to<Curve>();
+ if (curve)
+ curve_ref = Ref<Curve>(curve);
+ else {
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ ct->connect(CoreStringNames::get_singleton()->changed, this, "_curve_texture_changed");
+ curve_ref = ct->get_curve();
+ }
+ }
+ }
- ur->create_action(TTR("Modify Curve"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(this, "undo_redo_curve_texture", ppoints);
- ur->add_undo_method(this, "undo_redo_curve_texture", curve_texture_ref->get_points());
- ur->commit_action();
+ } else {
+ _current_ref = Ref<Resource>();
}
+
+ _view->set_curve(curve_ref);
}
-void CurveTextureEditorPlugin::_undo_redo_curve_texture(const PoolVector<Vector2> &points) {
+bool CurveEditorPlugin::handles(Object *p_object) const {
+ // Both handled so that we can keep the curve editor open
+ return p_object->cast_to<Curve>() || p_object->cast_to<CurveTexture>();
+}
- curve_texture_ref->set_points(points);
- curve_editor->set_points(Variant(curve_texture_ref->get_points()));
- curve_editor->update();
+void CurveEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ _toggle_button->show();
+ _editor_node->make_bottom_panel_item_visible(_view);
+ } else {
+ _toggle_button->hide();
+ if (_view->is_visible_in_tree())
+ _editor_node->hide_bottom_panel();
+ }
}
-CurveTextureEditorPlugin::~CurveTextureEditorPlugin() {
+void CurveEditorPlugin::_curve_texture_changed() {
+ // If the curve is shown indirectly as a CurveTexture is edited,
+ // we need to monitor when the curve property gets assigned
+ CurveTexture *ct = _current_ref->cast_to<CurveTexture>();
+ if (ct) {
+ _view->set_curve(ct->get_curve());
+ }
}
-void CurveTextureEditorPlugin::_bind_methods() {
- ClassDB::bind_method(D_METHOD("curve_changed"), &CurveTextureEditorPlugin::_curve_changed);
- ClassDB::bind_method(D_METHOD("_curve_settings_changed"), &CurveTextureEditorPlugin::_curve_settings_changed);
- ClassDB::bind_method(D_METHOD("undo_redo_curve_texture", "points"), &CurveTextureEditorPlugin::_undo_redo_curve_texture);
+void CurveEditorPlugin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_curve_texture_changed"), &CurveEditorPlugin::_curve_texture_changed);
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index 4e75ba407c..0ed4ee3517 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -27,69 +27,119 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#ifndef CURVE_EDITOR_PLUGIN_H
#define CURVE_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "scene/resources/curve.h"
-class CurveTextureEdit : public Control {
+// Edits a y(x) curve
+class CurveEditor : public Control {
+ GDCLASS(CurveEditor, Control)
+public:
+ CurveEditor();
- GDCLASS(CurveTextureEdit, Control);
+ Size2 get_minimum_size() const;
- struct Point {
+ void set_curve(Ref<Curve> curve);
- float offset;
- float height;
- bool operator<(const Point &p_ponit) const {
- return offset < p_ponit.offset;
- }
+ enum PresetID {
+ PRESET_FLAT0 = 0,
+ PRESET_FLAT1,
+ PRESET_LINEAR,
+ PRESET_EASE_IN,
+ PRESET_EASE_OUT,
+ PRESET_SMOOTHSTEP,
+ PRESET_COUNT
};
- bool grabbing;
- int grabbed;
- Vector<Point> points;
- float max, min;
+ enum ContextAction {
+ CONTEXT_ADD_POINT = 0,
+ CONTEXT_REMOVE_POINT
+ };
- void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d);
+ enum TangentIndex {
+ TANGENT_NONE = -1,
+ TANGENT_LEFT = 0,
+ TANGENT_RIGHT = 1
+ };
protected:
- void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
+
static void _bind_methods();
-public:
- void set_range(float p_min, float p_max);
- void set_points(const Vector<Vector2> &p_points);
- Vector<Vector2> get_points() const;
- virtual Size2 get_minimum_size() const;
- CurveTextureEdit();
+private:
+ void on_gui_input(const Ref<InputEvent> &p_event);
+ void on_preset_item_selected(int preset_id);
+ void _curve_changed();
+ void on_context_menu_item_selected(int action_id);
+
+ void open_context_menu(Vector2 pos);
+ int get_point_at(Vector2 pos) const;
+ int get_tangent_at(Vector2 pos) const;
+ void add_point(Vector2 pos);
+ void remove_point(int index);
+ void set_selected_point(int index);
+ void set_hover_point_index(int index);
+ void push_undo(Array previous_curve_data);
+ void update_view_transform();
+
+ Vector2 get_tangent_view_pos(int i, TangentIndex tangent) const;
+ Vector2 get_view_pos(Vector2 world_pos) const;
+ Vector2 get_world_pos(Vector2 view_pos) const;
+
+ void _draw();
+
+ void stroke_rect(Rect2 rect, Color color);
+
+private:
+ Rect2 _world_rect;
+ Transform2D _world_to_view;
+
+ Ref<Curve> _curve_ref;
+ PopupMenu *_context_menu;
+ PopupMenu *_presets_menu;
+
+ Array _undo_data;
+ bool _has_undo_data;
+ bool _undo_no_commit;
+
+ Vector2 _context_click_pos;
+ int _selected_point;
+ int _hover_point;
+ int _selected_tangent;
+ bool _dragging;
+
+ // Constant
+ float _hover_radius;
+ float _tangents_length;
};
-class CurveTextureEditorPlugin : public EditorPlugin {
-
- GDCLASS(CurveTextureEditorPlugin, EditorPlugin);
+class CurveEditorPlugin : public EditorPlugin {
+ GDCLASS(CurveEditorPlugin, EditorPlugin)
+public:
+ CurveEditorPlugin(EditorNode *p_node);
+ ~CurveEditorPlugin();
- CurveTextureEdit *curve_editor;
- Ref<CurveTexture> curve_texture_ref;
- EditorNode *editor;
- ToolButton *curve_button;
+ String get_name() const { return "Curve"; }
+ bool has_main_screen() const { return false; }
+ void edit(Object *p_object);
+ bool handles(Object *p_object) const;
+ void make_visible(bool p_visible);
-protected:
+private:
static void _bind_methods();
- void _curve_changed();
- void _undo_redo_curve_texture(const PoolVector<Vector2> &points);
- void _curve_settings_changed();
-public:
- virtual String get_name() const { return "CurveTexture"; }
- bool has_main_screen() const { return false; }
- virtual void edit(Object *p_node);
- virtual bool handles(Object *p_node) const;
- virtual void make_visible(bool p_visible);
+ void _curve_texture_changed();
- CurveTextureEditorPlugin(EditorNode *p_node);
- ~CurveTextureEditorPlugin();
+private:
+ CurveEditor *_view;
+ Ref<Resource> _current_ref;
+ EditorNode *_editor_node;
+ ToolButton *_toggle_button;
};
#endif // CURVE_EDITOR_PLUGIN_H
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index c6c85d8be2..a759e5892f 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -31,8 +31,8 @@
#include "canvas_item_editor_plugin.h"
#include "io/image_loader.h"
+#include "scene/3d/particles.h"
#include "scene/gui/separator.h"
-
void Particles2DEditorPlugin::edit(Object *p_object) {
if (p_object) {
@@ -62,78 +62,264 @@ void Particles2DEditorPlugin::_file_selected(const String &p_file) {
print_line("file: " + p_file);
- int epc = epoints->get_value();
+ source_emission_file = p_file;
+ emission_mask->popup_centered_minsize();
+}
+
+void Particles2DEditorPlugin::_menu_callback(int p_idx) {
+
+ switch (p_idx) {
+ case MENU_GENERATE_VISIBILITY_RECT: {
+ generate_aabb->popup_centered_minsize();
+ } break;
+ case MENU_LOAD_EMISSION_MASK: {
+
+ file->popup_centered_ratio();
+
+ } break;
+ case MENU_CLEAR_EMISSION_MASK: {
+
+ emission_mask->popup_centered_minsize();
+
+ /*undo_redo->create_action(TTR("Clear Emission Mask"));
+ undo_redo->add_do_method(particles, "set_emission_points", PoolVector<Vector2>());
+ undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
+ undo_redo->commit_action();*/
+ } break;
+ }
+}
+
+void Particles2DEditorPlugin::_generate_visibility_rect() {
+
+ float time = generate_seconds->get_value();
+
+ float running = 0.0;
+
+ EditorProgress ep("gen_aabb", TTR("Generating AABB"), int(time));
+
+ Rect2 rect;
+ while (running < time) {
+
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ ep.step("Generating..", int(running), true);
+ OS::get_singleton()->delay_usec(1000);
+
+ Rect2 capture = particles->capture_rect();
+ if (rect == Rect2())
+ rect = capture;
+ else
+ rect = rect.merge(capture);
+
+ running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
+ }
+
+ particles->set_visibility_rect(rect);
+}
+
+void Particles2DEditorPlugin::_generate_emission_mask() {
+
+ Ref<ParticlesMaterial> pm = particles->get_process_material();
+ if (!pm.is_valid()) {
+ EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material"));
+ return;
+ }
Ref<Image> img;
img.instance();
- Error err = ImageLoader::load_image(p_file, img);
- ERR_EXPLAIN(TTR("Error loading image:") + " " + p_file);
+ Error err = ImageLoader::load_image(source_emission_file, img);
+ ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file);
ERR_FAIL_COND(err != OK);
- img->convert(Image::FORMAT_LA8);
- ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8);
+ if (img->is_compressed()) {
+ img->decompress();
+ }
+ img->convert(Image::FORMAT_RGBA8);
+ ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
Size2i s = Size2(img->get_width(), img->get_height());
ERR_FAIL_COND(s.width == 0 || s.height == 0);
- PoolVector<uint8_t> data = img->get_data();
- PoolVector<uint8_t>::Read r = data.read();
+ Vector<Point2> valid_positions;
+ Vector<Point2> valid_normals;
+ Vector<uint8_t> valid_colors;
- Vector<Point2i> valid_positions;
valid_positions.resize(s.width * s.height);
+
+ EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected();
+
+ if (emode == EMISSION_MODE_BORDER_DIRECTED) {
+ valid_normals.resize(s.width * s.height);
+ }
+
+ bool capture_colors = emission_colors->is_pressed();
+
+ if (capture_colors) {
+ valid_colors.resize(s.width * s.height * 4);
+ }
+
int vpc = 0;
- for (int i = 0; i < s.width * s.height; i++) {
+ {
+ PoolVector<uint8_t> data = img->get_data();
+ PoolVector<uint8_t>::Read r = data.read();
+
+ for (int i = 0; i < s.width; i++) {
+ for (int j = 0; j < s.height; j++) {
+
+ uint8_t a = r[(j * s.width + i) * 4 + 3];
+
+ if (a > 128) {
+
+ if (emode == EMISSION_MODE_SOLID) {
+
+ if (capture_colors) {
+ valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
+ valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
+ valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
+ valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
+ }
+ valid_positions[vpc++] = Point2(i, j);
+
+ } else {
- uint8_t a = r[i * 2 + 1];
- if (a > 128) {
- valid_positions[vpc++] = Point2i(i % s.width, i / s.width);
+ bool on_border = false;
+ for (int x = i - 1; x <= i + 1; x++) {
+ for (int y = j - 1; y <= j + 1; y++) {
+
+ if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
+ on_border = true;
+ break;
+ }
+ }
+
+ if (on_border)
+ break;
+ }
+
+ if (on_border) {
+ valid_positions[vpc] = Point2(i, j);
+
+ if (emode == EMISSION_MODE_BORDER_DIRECTED) {
+ Vector2 normal;
+ for (int x = i - 2; x <= i + 2; x++) {
+ for (int y = j - 2; y <= j + 2; y++) {
+
+ if (x == i && y == j)
+ continue;
+
+ if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) {
+ normal += Vector2(x - i, y - j).normalized();
+ }
+ }
+ }
+
+ normal.normalize();
+ valid_normals[vpc] = normal;
+ }
+
+ if (capture_colors) {
+ valid_colors[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0];
+ valid_colors[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1];
+ valid_colors[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2];
+ valid_colors[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3];
+ }
+
+ vpc++;
+ }
+ }
+ }
+ }
}
}
valid_positions.resize(vpc);
+ if (valid_normals.size()) {
+ valid_normals.resize(vpc);
+ }
ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
ERR_FAIL_COND(valid_positions.size() == 0);
- PoolVector<Point2> epoints;
- epoints.resize(epc);
- PoolVector<Point2>::Write w = epoints.write();
+ PoolVector<uint8_t> texdata;
+
+ int w = 2048;
+ int h = (vpc / 2048) + 1;
- Size2 extents = Size2(img->get_width() * 0.5, img->get_height() * 0.5);
+ texdata.resize(w * h * 2 * sizeof(float));
- for (int i = 0; i < epc; i++) {
+ {
+ PoolVector<uint8_t>::Write tw = texdata.write();
+ float *twf = (float *)tw.ptr();
+ for (int i = 0; i < vpc; i++) {
- Point2 p = valid_positions[Math::rand() % vpc];
- p -= s / 2;
- w[i] = p / extents;
+ twf[i * 2 + 0] = valid_positions[i].x;
+ twf[i * 2 + 1] = valid_positions[i].y;
+ }
}
- w = PoolVector<Point2>::Write();
+ img.instance();
+ img->create(w, h, false, Image::FORMAT_RGF, texdata);
- undo_redo->create_action(TTR("Set Emission Mask"));
- undo_redo->add_do_method(particles, "set_emission_points", epoints);
- undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
- undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
- undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
- undo_redo->commit_action();
-}
+ Ref<ImageTexture> imgt;
+ imgt.instance();
+ imgt->create_from_image(img, 0);
-void Particles2DEditorPlugin::_menu_callback(int p_idx) {
+ pm->set_emission_point_texture(imgt);
+ pm->set_emission_point_count(vpc);
- switch (p_idx) {
- case MENU_LOAD_EMISSION_MASK: {
+ if (capture_colors) {
- file->popup_centered_ratio();
+ PoolVector<uint8_t> colordata;
+ colordata.resize(w * h * 4); //use RG texture
- } break;
- case MENU_CLEAR_EMISSION_MASK: {
+ {
+ PoolVector<uint8_t>::Write tw = colordata.write();
+ for (int i = 0; i < vpc * 4; i++) {
- undo_redo->create_action(TTR("Clear Emission Mask"));
- undo_redo->add_do_method(particles, "set_emission_points", PoolVector<Vector2>());
- undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
- undo_redo->commit_action();
- } break;
+ tw[i] = valid_colors[i];
+ }
+ }
+
+ img.instance();
+ img->create(w, h, false, Image::FORMAT_RGBA8, colordata);
+
+ imgt.instance();
+ imgt->create_from_image(img, 0);
+ pm->set_emission_color_texture(imgt);
}
+
+ if (valid_normals.size()) {
+ pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS);
+
+ PoolVector<uint8_t> normdata;
+ normdata.resize(w * h * 2 * sizeof(float)); //use RG texture
+
+ {
+ PoolVector<uint8_t>::Write tw = normdata.write();
+ float *twf = (float *)tw.ptr();
+ for (int i = 0; i < vpc; i++) {
+ twf[i * 2 + 0] = valid_normals[i].x;
+ twf[i * 2 + 1] = valid_normals[i].y;
+ }
+ }
+
+ img.instance();
+ img->create(w, h, false, Image::FORMAT_RGF, normdata);
+
+ imgt.instance();
+ imgt->create_from_image(img, 0);
+ pm->set_emission_normal_texture(imgt);
+
+ } else {
+ pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS);
+ }
+
+ /*undo_redo->create_action(TTR("Set Emission Mask"));
+ undo_redo->add_do_method(particles, "set_emission_points", epoints);
+ undo_redo->add_do_method(particles, "set_emission_half_extents", extents);
+ undo_redo->add_undo_method(particles, "set_emission_points", particles->get_emission_points());
+ undo_redo->add_undo_method(particles, "set_emission_half_extents", particles->get_emission_half_extents());
+ undo_redo->commit_action();
+ */
}
void Particles2DEditorPlugin::_notification(int p_what) {
@@ -150,6 +336,8 @@ void Particles2DEditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("_menu_callback"), &Particles2DEditorPlugin::_menu_callback);
ClassDB::bind_method(D_METHOD("_file_selected"), &Particles2DEditorPlugin::_file_selected);
+ ClassDB::bind_method(D_METHOD("_generate_visibility_rect"), &Particles2DEditorPlugin::_generate_visibility_rect);
+ ClassDB::bind_method(D_METHOD("_generate_emission_mask"), &Particles2DEditorPlugin::_generate_emission_mask);
}
Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
@@ -165,8 +353,10 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
toolbar->add_child(memnew(VSeparator));
menu = memnew(MenuButton);
+ menu->get_popup()->add_item(TTR("Generate Visibility Rect"), MENU_GENERATE_VISIBILITY_RECT);
+ menu->get_popup()->add_separator();
menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK);
- menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
+ // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK);
menu->set_text("Particles");
toolbar->add_child(menu);
@@ -185,6 +375,37 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) {
epoints->set_step(1);
epoints->set_value(512);
file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints);
+
+ generate_aabb = memnew(ConfirmationDialog);
+ generate_aabb->set_title(TTR("Generate Visibility Rect"));
+ VBoxContainer *genvb = memnew(VBoxContainer);
+ generate_aabb->add_child(genvb);
+ generate_seconds = memnew(SpinBox);
+ genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
+ generate_seconds->set_min(0.1);
+ generate_seconds->set_max(25);
+ generate_seconds->set_value(2);
+
+ toolbar->add_child(generate_aabb);
+
+ generate_aabb->connect("confirmed", this, "_generate_visibility_rect");
+
+ emission_mask = memnew(ConfirmationDialog);
+ emission_mask->set_title(TTR("Generate Visibility Rect"));
+ VBoxContainer *emvb = memnew(VBoxContainer);
+ emission_mask->add_child(emvb);
+ emission_mask_mode = memnew(OptionButton);
+ emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode);
+ emission_mask_mode->add_item("Solid Pixels", EMISSION_MODE_SOLID);
+ emission_mask_mode->add_item("Border Pixels", EMISSION_MODE_BORDER);
+ emission_mask_mode->add_item("Directed Border Pixels", EMISSION_MODE_BORDER_DIRECTED);
+ emission_colors = memnew(CheckBox);
+ emission_colors->set_text(TTR("Capture from Pixel"));
+ emvb->add_margin_child(TTR("Emission Colors"), emission_colors);
+
+ toolbar->add_child(emission_mask);
+
+ emission_mask->connect("confirmed", this, "_generate_emission_mask");
}
Particles2DEditorPlugin::~Particles2DEditorPlugin() {
diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h
index e532157c35..cea60fbeaf 100644
--- a/editor/plugins/particles_2d_editor_plugin.h
+++ b/editor/plugins/particles_2d_editor_plugin.h
@@ -44,10 +44,17 @@ class Particles2DEditorPlugin : public EditorPlugin {
enum {
+ MENU_GENERATE_VISIBILITY_RECT,
MENU_LOAD_EMISSION_MASK,
MENU_CLEAR_EMISSION_MASK
};
+ enum EmissionMode {
+ EMISSION_MODE_SOLID,
+ EMISSION_MODE_BORDER,
+ EMISSION_MODE_BORDER_DIRECTED
+ };
+
Particles2D *particles;
EditorFileDialog *file;
@@ -58,9 +65,20 @@ class Particles2DEditorPlugin : public EditorPlugin {
SpinBox *epoints;
+ ConfirmationDialog *generate_aabb;
+ SpinBox *generate_seconds;
+
+ ConfirmationDialog *emission_mask;
+ OptionButton *emission_mask_mode;
+ CheckBox *emission_colors;
+
+ String source_emission_file;
+
UndoRedo *undo_redo;
void _file_selected(const String &p_file);
void _menu_callback(int p_idx);
+ void _generate_visibility_rect();
+ void _generate_emission_mask();
protected:
void _notification(int p_what);
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 7c8ee97f22..bad88979ac 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -188,7 +188,7 @@ void ShaderTextEditor::_validate_script() {
if (err != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
set_error(error_text);
- get_text_edit()->set_line_as_marked(sl.get_error_line(), true);
+ get_text_edit()->set_line_as_marked(sl.get_error_line() - 1, true);
} else {
for (int i = 0; i < get_text_edit()->get_line_count(); i++)
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 676e50d61c..c4fe15e61c 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -61,9 +61,21 @@ void TextureEditor::_notification(int p_what) {
tex_height = texture->get_height() * tex_width / texture->get_width();
}
+ // Prevent the texture from being unpreviewable after the rescale, so that we can still see something
+ if (tex_height <= 0)
+ tex_height = 1;
+ if (tex_width <= 0)
+ tex_width = 1;
+
int ofs_x = (size.width - tex_width) / 2;
int ofs_y = (size.height - tex_height) / 2;
+ if (texture->cast_to<CurveTexture>()) {
+ // In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient
+ ofs_y = 0;
+ tex_height = size.height;
+ }
+
draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
Ref<Font> font = get_font("font", "Label");
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 976a7b6271..8a7dcea393 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -218,7 +218,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
mtx.xform(rect.position + Vector2(0, rect.size.y / 2)) + Vector2(-4, 0)
};
- Ref<InputEventMouseButton> mb;
+ Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
if (mb->get_button_index() == BUTTON_LEFT) {
@@ -792,6 +792,7 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
hb_tools->add_child(snap_mode_button);
snap_mode_button->set_text(TTR("<None>"));
PopupMenu *p = snap_mode_button->get_popup();
+ p->set_hide_on_checkable_item_selection(false);
p->add_item(TTR("<None>"), 0);
p->add_item(TTR("Pixel Snap"), 1);
p->add_item(TTR("Grid Snap"), 2);
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 0b088f7171..14b25681b7 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -105,13 +105,16 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
if (!child2->cast_to<StaticBody2D>())
continue;
StaticBody2D *sb = child2->cast_to<StaticBody2D>();
- int shape_count = sb->get_shape_count();
- if (shape_count == 0)
- continue;
- for (int shape_index = 0; shape_index < shape_count; ++shape_index) {
- Ref<Shape2D> collision = sb->get_shape(shape_index);
- if (collision.is_valid()) {
- collisions.push_back(collision);
+
+ List<uint32_t> shapes;
+ sb->get_shape_owners(&shapes);
+
+ for (List<uint32_t>::Element *E = shapes.front(); E; E = E->next()) {
+
+ for (int k = 0; k < sb->shape_owner_get_shape_count(E->get()); k++) {
+
+ Ref<Shape> shape = sb->shape_owner_get_shape(E->get(), k);
+ collisions.push_back(shape); //uh what about transform?
}
}
}
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index d58454a223..355f8ba22e 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -235,7 +235,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_button->set_disabled(true);
} else {
- export_error->show();
+ export_error->hide();
export_templates_error->hide();
export_button->set_disabled(false);
}
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index dbd0758256..0f3f5500a8 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -245,7 +245,7 @@ void ScriptCreateDialog::_lang_changed(int l) {
template_menu->clear();
template_menu->add_item(TTR("Default"));
for (int i = 0; i < template_list.size(); i++) {
- template_menu->add_item(template_list[i]);
+ template_menu->add_item(template_list[i].capitalize());
}
}
diff --git a/main/input_default.cpp b/main/input_default.cpp
index e488438059..bde1e84926 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -874,6 +874,8 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) {
_THREAD_SAFE_METHOD_;
+ ERR_FAIL_INDEX(p_axis, JOY_AXIS_MAX);
+
Joypad &joy = joy_names[p_device];
if (joy.last_axis[p_axis] == p_value.value) {
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 5e3ce31dd6..adf3c8edc4 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -1276,7 +1276,7 @@ static void _find_identifiers_in_class(GDCompletionContext &context, bool p_stat
}
}
List<MethodInfo> methods;
- ClassDB::get_method_list(type, &methods);
+ ClassDB::get_method_list(type, &methods, false, true);
for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
if (E->get().name.begins_with("_"))
continue;
@@ -1643,7 +1643,7 @@ static void _find_type_arguments(GDCompletionContext &context, const GDParser::N
} else {
//regular method
- if (p_method.operator String() == "connect") {
+ if (p_method.operator String() == "connect" || (p_method.operator String() == "emit_signal" && p_argidx == 0)) {
if (p_argidx == 0) {
List<MethodInfo> sigs;
@@ -2251,7 +2251,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
List<MethodInfo> mi;
- ClassDB::get_method_list(t.obj_type, &mi);
+ ClassDB::get_method_list(t.obj_type, &mi, false, true);
for (List<MethodInfo>::Element *E = mi.front(); E; E = E->next()) {
if (E->get().name.begins_with("_"))
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 2033bc76a1..474d286405 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -27,6 +27,7 @@
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+
#include "export.h"
#include "editor/editor_export.h"
#include "editor/editor_node.h"
@@ -40,381 +41,283 @@
#include "platform/osx/logo.h"
#include "string.h"
#include "version.h"
-#if 0
class EditorExportPlatformOSX : public EditorExportPlatform {
- GDCLASS( EditorExportPlatformOSX,EditorExportPlatform );
-
- String custom_release_package;
- String custom_debug_package;
-
- enum BitsMode {
- BITS_FAT,
- BITS_64,
- BITS_32
- };
+ GDCLASS(EditorExportPlatformOSX, EditorExportPlatform);
int version_code;
- String app_name;
- String info;
- String icon;
- String identifier;
- String short_version;
- String version;
- String signature;
- String copyright;
- BitsMode bits_mode;
- bool high_resolution;
-
Ref<ImageTexture> logo;
- void _fix_plist(Vector<uint8_t>& plist, const String &p_binary);
- void _make_icon(const Image& p_icon,Vector<uint8_t>& data);
-
+ void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
+ void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
protected:
-
- bool _set(const StringName& p_name, const Variant& p_value);
- bool _get(const StringName& p_name,Variant &r_ret) const;
- void _get_property_list( List<PropertyInfo> *p_list) const;
+ virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features);
+ virtual void get_export_options(List<ExportOption> *r_options);
public:
-
virtual String get_name() const { return "Mac OSX"; }
- virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; }
virtual Ref<Texture> get_logo() const { return logo; }
-
- virtual bool poll_devices() { return false;}
- virtual int get_device_count() const { return 0; }
- virtual String get_device_name(int p_device) const { return String(); }
- virtual String get_device_info(int p_device) const { return String(); }
- virtual Error run(int p_device,int p_flags=0);
-
- virtual bool requires_password(bool p_debug) const { return false; }
virtual String get_binary_extension() const { return "zip"; }
- virtual Error export_project(const String& p_path,bool p_debug,int p_flags=0);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
- virtual bool can_export(String *r_error=NULL) const;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
EditorExportPlatformOSX();
~EditorExportPlatformOSX();
};
-bool EditorExportPlatformOSX::_set(const StringName& p_name, const Variant& p_value) {
-
- String n=p_name;
-
- if (n=="custom_package/debug")
- custom_debug_package=p_value;
- else if (n=="custom_package/release")
- custom_release_package=p_value;
- else if (n=="application/name")
- app_name=p_value;
- else if (n=="application/info")
- info=p_value;
- else if (n=="application/icon")
- icon=p_value;
- else if (n=="application/identifier")
- identifier=p_value;
- else if (n=="application/signature")
- signature=p_value;
- else if (n=="application/short_version")
- short_version=p_value;
- else if (n=="application/version")
- version=p_value;
- else if (n=="application/copyright")
- copyright=p_value;
- else if (n=="application/bits_mode")
- bits_mode=BitsMode(int(p_value));
- else if (n=="display/high_res")
- high_resolution=p_value;
- else
- return false;
-
- return true;
-}
-
-bool EditorExportPlatformOSX::_get(const StringName& p_name,Variant &r_ret) const{
-
- String n=p_name;
-
- if (n=="custom_package/debug")
- r_ret=custom_debug_package;
- else if (n=="custom_package/release")
- r_ret=custom_release_package;
- else if (n=="application/name")
- r_ret=app_name;
- else if (n=="application/info")
- r_ret=info;
- else if (n=="application/icon")
- r_ret=icon;
- else if (n=="application/identifier")
- r_ret=identifier;
- else if (n=="application/signature")
- r_ret=signature;
- else if (n=="application/short_version")
- r_ret=short_version;
- else if (n=="application/version")
- r_ret=version;
- else if (n=="application/copyright")
- r_ret=copyright;
- else if (n=="application/bits_mode")
- r_ret=bits_mode;
- else if (n=="display/high_res")
- r_ret=high_resolution;
- else
- return false;
+void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) {
- return true;
+ // what does this need to do?
}
-void EditorExportPlatformOSX::_get_property_list( List<PropertyInfo> *p_list) const{
-
- p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE,"zip"));
- p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE,"zip"));
-
- p_list->push_back( PropertyInfo( Variant::STRING, "application/name") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/info") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/icon",PROPERTY_HINT_FILE,"png") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/identifier") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/signature") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/short_version") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/version") );
- p_list->push_back( PropertyInfo( Variant::STRING, "application/copyright") );
- p_list->push_back( PropertyInfo( Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits") );
- p_list->push_back( PropertyInfo( Variant::BOOL, "display/high_res") );
+void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) {
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
+
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier"), "org.godotengine.macgame"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "godotmacgame"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0"));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/bits_mode", PROPERTY_HINT_ENUM, "Fat (32 & 64 bits),64 bits,32 bits"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false));
}
-void EditorExportPlatformOSX::_make_icon(const Image& p_icon,Vector<uint8_t>& icon) {
+void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) {
-
- Ref<ImageTexture> it = memnew( ImageTexture );
- int size=512;
+ Ref<ImageTexture> it = memnew(ImageTexture);
+ int size = 512;
Vector<uint8_t> data;
data.resize(8);
- data[0]='i';
- data[1]='c';
- data[2]='n';
- data[3]='s';
+ data[0] = 'i';
+ data[1] = 'c';
+ data[2] = 'n';
+ data[3] = 's';
- const char *name[]={"ic09","ic08","ic07","icp6","icp5","icp4"};
- int index=0;
+ const char *name[] = { "ic09", "ic08", "ic07", "icp6", "icp5", "icp4" };
+ int index = 0;
- while(size>=16) {
+ while (size >= 16) {
- Image copy = p_icon;
- copy.convert(Image::FORMAT_RGBA8);
- copy.resize(size,size);
+ Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
+ copy->convert(Image::FORMAT_RGBA8);
+ copy->resize(size, size);
it->create_from_image(copy);
- String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/icon.png";
- ResourceSaver::save(path,it);
+ String path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/icon.png";
+ ResourceSaver::save(path, it);
- FileAccess *f = FileAccess::open(path,FileAccess::READ);
+ FileAccess *f = FileAccess::open(path, FileAccess::READ);
ERR_FAIL_COND(!f);
int ofs = data.size();
uint32_t len = f->get_len();
- data.resize(data.size()+len+8);
- f->get_buffer(&data[ofs+8],len);
+ data.resize(data.size() + len + 8);
+ f->get_buffer(&data[ofs + 8], len);
memdelete(f);
- len+=8;
- len=BSWAP32(len);
- copymem(&data[ofs],name[index],4);
- encode_uint32(len,&data[ofs+4]);
+ len += 8;
+ len = BSWAP32(len);
+ copymem(&data[ofs], name[index], 4);
+ encode_uint32(len, &data[ofs + 4]);
index++;
- size/=2;
+ size /= 2;
}
uint32_t total_len = data.size();
total_len = BSWAP32(total_len);
- encode_uint32(total_len,&data[4]);
+ encode_uint32(total_len, &data[4]);
- icon=data;
+ p_data = data;
}
-
-void EditorExportPlatformOSX::_fix_plist(Vector<uint8_t>& plist,const String& p_binary) {
-
+void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) {
String str;
String strnew;
- str.parse_utf8((const char*)plist.ptr(),plist.size());
- Vector<String> lines=str.split("\n");
- for(int i=0;i<lines.size();i++) {
- if (lines[i].find("$binary")!=-1) {
- strnew+=lines[i].replace("$binary",p_binary)+"\n";
- } else if (lines[i].find("$name")!=-1) {
- strnew+=lines[i].replace("$name",p_binary)+"\n";
- } else if (lines[i].find("$info")!=-1) {
- strnew+=lines[i].replace("$info",info)+"\n";
- } else if (lines[i].find("$identifier")!=-1) {
- strnew+=lines[i].replace("$identifier",identifier)+"\n";
- } else if (lines[i].find("$short_version")!=-1) {
- strnew+=lines[i].replace("$short_version",short_version)+"\n";
- } else if (lines[i].find("$version")!=-1) {
- strnew+=lines[i].replace("$version",version)+"\n";
- } else if (lines[i].find("$signature")!=-1) {
- strnew+=lines[i].replace("$signature",signature)+"\n";
- } else if (lines[i].find("$copyright")!=-1) {
- strnew+=lines[i].replace("$copyright",copyright)+"\n";
- } else if (lines[i].find("$highres")!=-1) {
- strnew+=lines[i].replace("$highres",high_resolution?"<true/>":"<false/>")+"\n";
+ str.parse_utf8((const char *)plist.ptr(), plist.size());
+ Vector<String> lines = str.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (lines[i].find("$binary") != -1) {
+ strnew += lines[i].replace("$binary", p_binary) + "\n";
+ } else if (lines[i].find("$name") != -1) {
+ strnew += lines[i].replace("$name", p_binary) + "\n";
+ } else if (lines[i].find("$info") != -1) {
+ strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n";
+ } else if (lines[i].find("$identifier") != -1) {
+ strnew += lines[i].replace("$identifier", p_preset->get("application/identifier")) + "\n";
+ } else if (lines[i].find("$short_version") != -1) {
+ strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n";
+ } else if (lines[i].find("$version") != -1) {
+ strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n";
+ } else if (lines[i].find("$signature") != -1) {
+ strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n";
+ } else if (lines[i].find("$copyright") != -1) {
+ strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
+ } else if (lines[i].find("$highres") != -1) {
+ strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n";
} else {
- strnew+=lines[i]+"\n";
+ strnew += lines[i] + "\n";
}
}
CharString cs = strnew.utf8();
plist.resize(cs.size());
- for(int i=9;i<cs.size();i++) {
- plist[i]=cs[i];
+ for (int i = 9; i < cs.size(); i++) {
+ plist[i] = cs[i];
}
}
-Error EditorExportPlatformOSX::export_project(const String& p_path, bool p_debug, int p_flags) {
+Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
String src_pkg;
- EditorProgress ep("export","Exporting for OSX",104);
-
+ EditorProgress ep("export", "Exporting for OSX", 104);
if (p_debug)
- src_pkg=custom_debug_package;
+ src_pkg = p_preset->get("custom_package/debug");
else
- src_pkg=custom_release_package;
+ src_pkg = p_preset->get("custom_package/release");
- if (src_pkg=="") {
+ if (src_pkg == "") {
String err;
- src_pkg=find_export_template("osx.zip", &err);
- if (src_pkg=="") {
+ src_pkg = find_export_template("osx.zip", &err);
+ if (src_pkg == "") {
EditorNode::add_io_error(err);
return ERR_FILE_NOT_FOUND;
}
}
-
- FileAccess *src_f=NULL;
+ FileAccess *src_f = NULL;
zlib_filefunc_def io = zipio_create_io_from_file(&src_f);
- ep.step("Creating app",0);
+ ep.step("Creating app", 0);
unzFile pkg = unzOpen2(src_pkg.utf8().get_data(), &io);
if (!pkg) {
- EditorNode::add_io_error("Could not find template app to export:\n"+src_pkg);
+ EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg);
return ERR_FILE_NOT_FOUND;
}
ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN);
int ret = unzGoToFirstFile(pkg);
- zlib_filefunc_def io2=io;
- FileAccess *dst_f=NULL;
- io2.opaque=&dst_f;
- zipFile dpkg=zipOpen2(p_path.utf8().get_data(),APPEND_STATUS_CREATE,NULL,&io2);
+ zlib_filefunc_def io2 = io;
+ FileAccess *dst_f = NULL;
+ io2.opaque = &dst_f;
+ zipFile dpkg = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, NULL, &io2);
String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".";
- binary_to_use += String(bits_mode==BITS_FAT ? "fat" : bits_mode==BITS_64 ? "64" : "32");
+ int bits_mode = p_preset->get("application/bits_mode");
+ binary_to_use += String(bits_mode == 0 ? "fat" : bits_mode == 1 ? "64" : "32");
- print_line("binary: "+binary_to_use);
+ print_line("binary: " + binary_to_use);
String pkg_name;
- if (app_name!="")
- pkg_name=app_name;
- else if (String(GlobalConfig::get_singleton()->get("application/name"))!="")
- pkg_name=String(GlobalConfig::get_singleton()->get("application/name"));
+ if (p_preset->get("application/name") != "")
+ pkg_name = p_preset->get("application/name"); // app_name
+ else if (String(GlobalConfig::get_singleton()->get("application/name")) != "")
+ pkg_name = String(GlobalConfig::get_singleton()->get("application/name"));
else
- pkg_name="Unnamed";
-
+ pkg_name = "Unnamed";
bool found_binary = false;
- while(ret==UNZ_OK) {
+ while (ret == UNZ_OK) {
//get filename
unz_file_info info;
char fname[16384];
- ret = unzGetCurrentFileInfo(pkg,&info,fname,16384,NULL,0,NULL,0);
+ ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);
- String file=fname;
+ String file = fname;
- print_line("READ: "+file);
+ print_line("READ: " + file);
Vector<uint8_t> data;
data.resize(info.uncompressed_size);
//read
unzOpenCurrentFile(pkg);
- unzReadCurrentFile(pkg,data.ptr(),data.size());
+ unzReadCurrentFile(pkg, data.ptr(), data.size());
unzCloseCurrentFile(pkg);
//write
- file = file.replace_first("osx_template.app/","");
+ file = file.replace_first("osx_template.app/", "");
-
- if (file=="Contents/Info.plist") {
+ if (file == "Contents/Info.plist") {
print_line("parse plist");
- _fix_plist(data,pkg_name);
+ _fix_plist(p_preset, data, pkg_name);
}
if (file.begins_with("Contents/MacOS/godot_")) {
- if (file!="Contents/MacOS/"+binary_to_use) {
+ if (file != "Contents/MacOS/" + binary_to_use) {
ret = unzGoToNextFile(pkg);
continue; //ignore!
}
found_binary = true;
- file="Contents/MacOS/"+pkg_name;
+ file = "Contents/MacOS/" + pkg_name;
}
- if (file=="Contents/Resources/icon.icns") {
+ if (file == "Contents/Resources/icon.icns") {
//see if there is an icon
- String iconpath = GlobalConfig::get_singleton()->get("application/icon");
- print_line("icon? "+iconpath);
- if (iconpath!="") {
- Image icon;
- icon.load(iconpath);
- if (!icon.empty()) {
+ String iconpath;
+ if (p_preset->get("application/icon") != "")
+ iconpath = p_preset->get("application/icon");
+ else
+ iconpath = GlobalConfig::get_singleton()->get("application/icon");
+ print_line("icon? " + iconpath);
+ if (iconpath != "") {
+ Ref<Image> icon;
+ icon.instance();
+ icon->load(iconpath);
+ if (!icon->empty()) {
print_line("loaded?");
- _make_icon(icon,data);
+ _make_icon(icon, data);
}
}
//bleh?
}
- file=pkg_name+".app/"+file;
+ file = pkg_name + ".app/" + file;
- if (data.size()>0) {
- print_line("ADDING: "+file+" size: "+itos(data.size()));
+ if (data.size() > 0) {
+ print_line("ADDING: " + file + " size: " + itos(data.size()));
zip_fileinfo fi;
- fi.tmz_date.tm_hour=info.tmu_date.tm_hour;
- fi.tmz_date.tm_min=info.tmu_date.tm_min;
- fi.tmz_date.tm_sec=info.tmu_date.tm_sec;
- fi.tmz_date.tm_mon=info.tmu_date.tm_mon;
- fi.tmz_date.tm_mday=info.tmu_date.tm_mday;
- fi.tmz_date.tm_year=info.tmu_date.tm_year;
- fi.dosDate=info.dosDate;
- fi.internal_fa=info.internal_fa;
- fi.external_fa=info.external_fa;
+ fi.tmz_date.tm_hour = info.tmu_date.tm_hour;
+ fi.tmz_date.tm_min = info.tmu_date.tm_min;
+ fi.tmz_date.tm_sec = info.tmu_date.tm_sec;
+ fi.tmz_date.tm_mon = info.tmu_date.tm_mon;
+ fi.tmz_date.tm_mday = info.tmu_date.tm_mday;
+ fi.tmz_date.tm_year = info.tmu_date.tm_year;
+ fi.dosDate = info.dosDate;
+ fi.internal_fa = info.internal_fa;
+ fi.external_fa = info.external_fa;
int err = zipOpenNewFileInZip(dpkg,
- file.utf8().get_data(),
- &fi,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
- print_line("OPEN ERR: "+itos(err));
- err = zipWriteInFileInZip(dpkg,data.ptr(),data.size());
- print_line("WRITE ERR: "+itos(err));
+ file.utf8().get_data(),
+ &fi,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+
+ print_line("OPEN ERR: " + itos(err));
+ err = zipWriteInFileInZip(dpkg, data.ptr(), data.size());
+ print_line("WRITE ERR: " + itos(err));
zipCloseFileInZip(dpkg);
}
@@ -422,126 +325,98 @@ Error EditorExportPlatformOSX::export_project(const String& p_path, bool p_debug
}
if (!found_binary) {
- ERR_PRINTS("Requested template binary '"+binary_to_use+"' not found. It might be missing from your template archive.");
- zipClose(dpkg,NULL);
+ ERR_PRINTS("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive.");
+ zipClose(dpkg, NULL);
unzClose(pkg);
return ERR_FILE_NOT_FOUND;
}
+ ep.step("Making PKG", 1);
- ep.step("Making PKG",1);
-
- String pack_path=EditorSettings::get_singleton()->get_settings_path()+"/tmp/data.pck";
- FileAccess *pfs = FileAccess::open(pack_path,FileAccess::WRITE);
- Error err = save_pack(pfs);
- memdelete(pfs);
+ String pack_path = EditorSettings::get_singleton()->get_settings_path() + "/tmp/data.pck";
+ Error err = save_pack(p_preset, pack_path);
if (err) {
- zipClose(dpkg,NULL);
+ zipClose(dpkg, NULL);
unzClose(pkg);
return err;
-
}
{
//write datapack
zipOpenNewFileInZip(dpkg,
- (pkg_name+".app/Contents/Resources/data.pck").utf8().get_data(),
- NULL,
- NULL,
- 0,
- NULL,
- 0,
- NULL,
- Z_DEFLATED,
- Z_DEFAULT_COMPRESSION);
-
-
- FileAccess *pf = FileAccess::open(pack_path,FileAccess::READ);
- ERR_FAIL_COND_V(!pf,ERR_CANT_OPEN);
+ (pkg_name + ".app/Contents/Resources/data.pck").utf8().get_data(),
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ NULL,
+ Z_DEFLATED,
+ Z_DEFAULT_COMPRESSION);
+
+ FileAccess *pf = FileAccess::open(pack_path, FileAccess::READ);
+ ERR_FAIL_COND_V(!pf, ERR_CANT_OPEN);
const int BSIZE = 16384;
uint8_t buf[BSIZE];
- while(true) {
+ while (true) {
- int r = pf->get_buffer(buf,BSIZE);
- if (r<=0)
+ int r = pf->get_buffer(buf, BSIZE);
+ if (r <= 0)
break;
- zipWriteInFileInZip(dpkg,buf,r);
-
+ zipWriteInFileInZip(dpkg, buf, r);
}
zipCloseFileInZip(dpkg);
memdelete(pf);
-
}
- zipClose(dpkg,NULL);
+ zipClose(dpkg, NULL);
unzClose(pkg);
return OK;
}
+bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
-Error EditorExportPlatformOSX::run(int p_device, int p_flags) {
-
- return OK;
-}
-
-
-EditorExportPlatformOSX::EditorExportPlatformOSX() {
-
- Image img( _osx_logo );
- logo = Ref<ImageTexture>( memnew( ImageTexture ));
- logo->create_from_image(img);
-
- info="Made with Godot Engine";
- identifier="org.godotengine.macgame";
- signature="godotmacgame";
- short_version="1.0";
- version="1.0";
- bits_mode=BITS_FAT;
- high_resolution=false;
-
-}
-
-bool EditorExportPlatformOSX::can_export(String *r_error) const {
-
-
- bool valid=true;
+ bool valid = true;
String err;
- if (!exists_export_template("osx.zip")) {
- valid=false;
- err+="No export templates found.\nDownload and install export templates.\n";
+ if (!exists_export_template("osx.zip", &err)) {
+ valid = false;
}
- if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) {
- valid=false;
- err+="Custom debug package not found.\n";
+ if (p_preset->get("custom_package/debug") != "" && !FileAccess::exists(p_preset->get("custom_package/debug"))) {
+ valid = false;
+ err += "Custom debug package not found.\n";
}
- if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) {
- valid=false;
- err+="Custom release package not found.\n";
+ if (p_preset->get("custom_package/release") != "" && !FileAccess::exists(p_preset->get("custom_package/release"))) {
+ valid = false;
+ err += "Custom release package not found.\n";
}
- if (r_error)
- *r_error=err;
+ if (!err.empty())
+ r_error = err;
return valid;
}
+EditorExportPlatformOSX::EditorExportPlatformOSX() {
-EditorExportPlatformOSX::~EditorExportPlatformOSX() {
+ Ref<Image> img = memnew(Image(_osx_logo));
+ logo.instance();
+ logo->create_from_image(img);
+}
+EditorExportPlatformOSX::~EditorExportPlatformOSX() {
}
-#endif
void register_osx_exporter() {
-#if 0
- Ref<EditorExportPlatformOSX> exporter = Ref<EditorExportPlatformOSX>( memnew(EditorExportPlatformOSX) );
- EditorImportExport::get_singleton()->add_export_platform(exporter);
-#endif
+ Ref<EditorExportPlatformOSX> platform;
+ platform.instance();
+
+ EditorExport::get_singleton()->add_export_platform(platform);
}
diff --git a/platform/windows/godot.ico b/platform/windows/godot.ico
index fd5c28944f..dd611e07da 100644
--- a/platform/windows/godot.ico
+++ b/platform/windows/godot.ico
Binary files differ
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 6a3238b64e..a1ab51b3c8 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -30,7 +30,6 @@
#include "animated_sprite.h"
#include "os/os.h"
#include "scene/scene_string_names.h"
-#include "scene/scene_string_names.h"
#define NORMAL_SUFFIX "_normal"
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 2274450647..80defac079 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -99,7 +99,7 @@ public:
const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name);
- if (p_idx >= EN->get().frames.size())
+ if (!EN || p_idx >= EN->get().frames.size())
return Ref<Texture>();
return EN->get().frames[p_idx];
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 3d9e64ae79..cef473dcdf 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -61,7 +61,7 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
- target[j] = buffer[j] * vol;
+ target[j] += buffer[j] * vol;
vol += vol_inc;
}
@@ -76,8 +76,8 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
AudioFrame frame = buffer[j] * vol;
- targets[0][j] = frame;
- targets[1][j] = frame;
+ targets[0][j] += frame;
+ targets[1][j] += frame;
vol += vol_inc;
}
@@ -93,9 +93,9 @@ void AudioStreamPlayer2D::_mix_audio() {
for (int j = 0; j < buffer_size; j++) {
AudioFrame frame = buffer[j] * vol;
- targets[0][j] = frame;
- targets[1][j] = frame;
- targets[2][j] = frame;
+ targets[0][j] += frame;
+ targets[1][j] += frame;
+ targets[2][j] += frame;
vol += vol_inc;
}
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 471f529713..189dd66a26 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -39,6 +39,196 @@
#include "scene/scene_string_names.h"
#include "servers/visual_server.h"
+Mutex *CanvasItemMaterial::material_mutex = NULL;
+SelfList<CanvasItemMaterial>::List CanvasItemMaterial::dirty_materials;
+Map<CanvasItemMaterial::MaterialKey, CanvasItemMaterial::ShaderData> CanvasItemMaterial::shader_map;
+
+void CanvasItemMaterial::init_shaders() {
+
+#ifndef NO_THREADS
+ material_mutex = Mutex::create();
+#endif
+}
+
+void CanvasItemMaterial::finish_shaders() {
+
+#ifndef NO_THREADS
+ memdelete(material_mutex);
+#endif
+}
+
+void CanvasItemMaterial::_update_shader() {
+
+ dirty_materials.remove(&element);
+
+ MaterialKey mk = _compute_key();
+ if (mk.key == current_key.key)
+ return; //no update required in the end
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ VS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+ }
+
+ current_key = mk;
+
+ if (shader_map.has(mk)) {
+
+ VS::get_singleton()->material_set_shader(_get_material(), shader_map[mk].shader);
+ shader_map[mk].users++;
+ return;
+ }
+
+ //must create a shader!
+
+ String code = "shader_type canvas_item;\nrender_mode ";
+ switch (blend_mode) {
+ case BLEND_MODE_MIX: code += "blend_mix"; break;
+ case BLEND_MODE_ADD: code += "blend_add"; break;
+ case BLEND_MODE_SUB: code += "blend_sub"; break;
+ case BLEND_MODE_MUL: code += "blend_mul"; break;
+ case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break;
+ }
+
+ switch (light_mode) {
+ case LIGHT_MODE_NORMAL: break;
+ case LIGHT_MODE_UNSHADED: code += "unshaded"; break;
+ case LIGHT_MODE_LIGHT_ONLY: code += "light_only"; break;
+ }
+ code += ";\n"; //thats it.
+
+ ShaderData shader_data;
+ shader_data.shader = VS::get_singleton()->shader_create();
+ shader_data.users = 1;
+
+ VS::get_singleton()->shader_set_code(shader_data.shader, code);
+
+ shader_map[mk] = shader_data;
+
+ VS::get_singleton()->material_set_shader(_get_material(), shader_data.shader);
+}
+
+void CanvasItemMaterial::flush_changes() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ while (dirty_materials.first()) {
+
+ dirty_materials.first()->self()->_update_shader();
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
+void CanvasItemMaterial::_queue_shader_change() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ if (!element.in_list()) {
+ dirty_materials.add(&element);
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
+bool CanvasItemMaterial::_is_shader_dirty() const {
+
+ bool dirty = false;
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ dirty = element.in_list();
+
+ if (material_mutex)
+ material_mutex->unlock();
+
+ return dirty;
+}
+void CanvasItemMaterial::set_blend_mode(BlendMode p_blend_mode) {
+
+ blend_mode = p_blend_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::BlendMode CanvasItemMaterial::get_blend_mode() const {
+ return blend_mode;
+}
+
+void CanvasItemMaterial::set_light_mode(LightMode p_light_mode) {
+
+ light_mode = p_light_mode;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::LightMode CanvasItemMaterial::get_light_mode() const {
+
+ return light_mode;
+}
+
+void CanvasItemMaterial::_validate_property(PropertyInfo &property) const {
+}
+
+void CanvasItemMaterial::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_blend_mode", "blend_mode"), &CanvasItemMaterial::set_blend_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_mode"), &CanvasItemMaterial::get_blend_mode);
+
+ ClassDB::bind_method(D_METHOD("set_light_mode", "light_mode"), &CanvasItemMaterial::set_light_mode);
+ ClassDB::bind_method(D_METHOD("get_light_mode"), &CanvasItemMaterial::get_light_mode);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
+
+ BIND_CONSTANT(BLEND_MODE_MIX);
+ BIND_CONSTANT(BLEND_MODE_ADD);
+ BIND_CONSTANT(BLEND_MODE_SUB);
+ BIND_CONSTANT(BLEND_MODE_MUL);
+ BIND_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+ BIND_CONSTANT(LIGHT_MODE_NORMAL);
+ BIND_CONSTANT(LIGHT_MODE_UNSHADED);
+ BIND_CONSTANT(LIGHT_MODE_LIGHT_ONLY);
+}
+
+CanvasItemMaterial::CanvasItemMaterial()
+ : element(this) {
+
+ blend_mode = BLEND_MODE_MIX;
+ light_mode = LIGHT_MODE_NORMAL;
+
+ current_key.key = 0;
+ current_key.invalid_key = 1;
+ _queue_shader_change();
+}
+
+CanvasItemMaterial::~CanvasItemMaterial() {
+
+ if (material_mutex)
+ material_mutex->lock();
+
+ if (shader_map.has(current_key)) {
+ shader_map[current_key].users--;
+ if (shader_map[current_key].users == 0) {
+ //deallocate shader, as it's no longer in use
+ VS::get_singleton()->free(shader_map[current_key].shader);
+ shader_map.erase(current_key);
+ }
+
+ VS::get_singleton()->material_set_shader(_get_material(), RID());
+ }
+
+ if (material_mutex)
+ material_mutex->unlock();
+}
+
///////////////////////////////////////////////////////////////////
bool CanvasItem::is_visible_in_tree() const {
@@ -417,14 +607,22 @@ void CanvasItem::draw_line(const Point2 &p_from, const Point2 &p_to, const Color
VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_from, p_to, p_color, p_width, p_antialiased);
}
-void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color) {
+void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled) {
if (!drawing) {
ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL();
}
- VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
+ if (p_filled) {
+
+ VisualServer::get_singleton()->canvas_item_add_rect(canvas_item, p_rect, p_color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(p_rect.size.width, 0), p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position, p_rect.position + Size2(0, p_rect.size.height), p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(0, p_rect.size.height), p_rect.position + p_rect.size, p_color);
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item, p_rect.position + Point2(p_rect.size.width, 0), p_rect.position + p_rect.size, p_color);
+ }
}
void CanvasItem::draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color) {
@@ -657,7 +855,7 @@ bool CanvasItem::is_draw_behind_parent_enabled() const {
return behind;
}
-void CanvasItem::set_material(const Ref<ShaderMaterial> &p_material) {
+void CanvasItem::set_material(const Ref<Material> &p_material) {
material = p_material;
RID rid;
@@ -678,7 +876,7 @@ bool CanvasItem::get_use_parent_material() const {
return use_parent_material;
}
-Ref<ShaderMaterial> CanvasItem::get_material() const {
+Ref<Material> CanvasItem::get_material() const {
return material;
}
@@ -754,7 +952,7 @@ void CanvasItem::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform);
ClassDB::bind_method(D_METHOD("draw_line", "from", "to", "color", "width", "antialiased"), &CanvasItem::draw_line, DEFVAL(1.0), DEFVAL(false));
- ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color"), &CanvasItem::draw_rect);
+ ClassDB::bind_method(D_METHOD("draw_rect", "rect", "color", "filled"), &CanvasItem::draw_rect, DEFVAL(true));
ClassDB::bind_method(D_METHOD("draw_circle", "pos", "radius", "color"), &CanvasItem::draw_circle);
ClassDB::bind_method(D_METHOD("draw_texture", "texture:Texture", "pos", "modulate", "normal_map:Texture"), &CanvasItem::draw_texture, DEFVAL(Color(1, 1, 1, 1)), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("draw_texture_rect", "texture:Texture", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &CanvasItem::draw_texture_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()));
@@ -780,8 +978,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d);
//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport);
- ClassDB::bind_method(D_METHOD("set_material", "material:ShaderMaterial"), &CanvasItem::set_material);
- ClassDB::bind_method(D_METHOD("get_material:ShaderMaterial"), &CanvasItem::get_material);
+ ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &CanvasItem::set_material);
+ ClassDB::bind_method(D_METHOD("get_material:Material"), &CanvasItem::get_material);
ClassDB::bind_method(D_METHOD("set_use_parent_material", "enable"), &CanvasItem::set_use_parent_material);
ClassDB::bind_method(D_METHOD("get_use_parent_material"), &CanvasItem::get_use_parent_material);
@@ -807,7 +1005,7 @@ void CanvasItem::_bind_methods() {
ADD_PROPERTYNO(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Material", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial"), "set_material", "get_material");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "use_parent_material"), "set_use_parent_material", "get_use_parent_material");
//exporting these two things doesn't really make much sense i think
//ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), "set_as_toplevel","is_set_as_toplevel") ;
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 595dd03057..bffc171fc1 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -42,6 +42,92 @@ class Font;
class StyleBox;
+class CanvasItemMaterial : public Material {
+
+ GDCLASS(CanvasItemMaterial, Material)
+
+public:
+ enum BlendMode {
+ BLEND_MODE_MIX,
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL,
+ BLEND_MODE_PREMULT_ALPHA
+ };
+
+ enum LightMode {
+ LIGHT_MODE_NORMAL,
+ LIGHT_MODE_UNSHADED,
+ LIGHT_MODE_LIGHT_ONLY
+ };
+
+private:
+ union MaterialKey {
+
+ struct {
+ uint32_t blend_mode : 4;
+ uint32_t light_mode : 4;
+ uint32_t invalid_key : 1;
+ };
+
+ uint32_t key;
+
+ bool operator<(const MaterialKey &p_key) const {
+ return key < p_key.key;
+ }
+ };
+
+ struct ShaderData {
+ RID shader;
+ int users;
+ };
+
+ static Map<MaterialKey, ShaderData> shader_map;
+
+ MaterialKey current_key;
+
+ _FORCE_INLINE_ MaterialKey _compute_key() const {
+
+ MaterialKey mk;
+ mk.key = 0;
+ mk.blend_mode = blend_mode;
+ mk.light_mode = light_mode;
+ return mk;
+ }
+
+ static Mutex *material_mutex;
+ static SelfList<CanvasItemMaterial>::List dirty_materials;
+ SelfList<CanvasItemMaterial> element;
+
+ void _update_shader();
+ _FORCE_INLINE_ void _queue_shader_change();
+ _FORCE_INLINE_ bool _is_shader_dirty() const;
+
+ BlendMode blend_mode;
+ LightMode light_mode;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+
+public:
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
+ void set_light_mode(LightMode p_light_mode);
+ LightMode get_light_mode() const;
+
+ static void init_shaders();
+ static void finish_shaders();
+ static void flush_changes();
+
+ CanvasItemMaterial();
+ virtual ~CanvasItemMaterial();
+};
+
+VARIANT_ENUM_CAST(CanvasItemMaterial::BlendMode)
+VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode)
+
class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
@@ -83,7 +169,7 @@ private:
bool notify_local_transform;
bool notify_transform;
- Ref<ShaderMaterial> material;
+ Ref<Material> material;
mutable Transform2D global_transform;
mutable bool global_invalid;
@@ -156,7 +242,7 @@ public:
/* DRAWING API */
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, float p_width = 1.0, bool p_antialiased = false);
- void draw_rect(const Rect2 &p_rect, const Color &p_color);
+ void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true);
void draw_circle(const Point2 &p_pos, float p_radius, const Color &p_color);
void draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1), const Ref<Texture> &p_normal_map = Ref<Texture>());
void draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, const Ref<Texture> &p_normal_map = Ref<Texture>());
@@ -203,8 +289,8 @@ public:
RID get_canvas() const;
Ref<World2D> get_world_2d() const;
- void set_material(const Ref<ShaderMaterial> &p_material);
- Ref<ShaderMaterial> get_material() const;
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
void set_use_parent_material(bool p_use_parent_material);
bool get_use_parent_material() const;
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index caf9cf201a..c5c274e225 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -31,18 +31,6 @@
#include "scene/scene_string_names.h"
#include "servers/physics_2d_server.h"
-void CollisionObject2D::_update_shapes_from_children() {
-
- shapes.clear();
- for (int i = 0; i < get_child_count(); i++) {
-
- Node *n = get_child(i);
- n->call("_add_to_collision_object", this);
- }
-
- _update_shapes();
-}
-
void CollisionObject2D::_notification(int p_what) {
switch (p_what) {
@@ -88,82 +76,197 @@ void CollisionObject2D::_notification(int p_what) {
}
}
-void CollisionObject2D::_update_shapes() {
+uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
- if (!rid.is_valid())
- return;
+ ShapeData sd;
+ uint32_t id;
+
+ if (shapes.size() == 0) {
+ id = 1;
+ } else {
+ id = shapes.back()->key() + 1;
+ }
+
+ sd.owner = p_owner;
+
+ shapes[id] = sd;
+
+ return id;
+}
+
+void CollisionObject2D::remove_shape_owner(uint32_t owner) {
+
+ ERR_FAIL_COND(!shapes.has(owner));
+
+ shape_owner_clear_shapes(owner);
+
+ shapes.erase(owner);
+}
+
+void CollisionObject2D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabled) {
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.disabled = p_disabled;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
+ } else {
+ Physics2DServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
+ }
+ }
+}
+
+bool CollisionObject2D::is_shape_owner_disabled(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), false);
+
+ return shapes[p_owner].disabled;
+}
+
+void CollisionObject2D::shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable) {
if (area)
- Physics2DServer::get_singleton()->area_clear_shapes(rid);
- else
- Physics2DServer::get_singleton()->body_clear_shapes(rid);
-
- for (int i = 0; i < shapes.size(); i++) {
-
- if (shapes[i].shape.is_null())
- continue;
- if (area)
- Physics2DServer::get_singleton()->area_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
- else {
- Physics2DServer::get_singleton()->body_add_shape(rid, shapes[i].shape->get_rid(), shapes[i].xform);
- if (shapes[i].trigger)
- Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, i, shapes[i].trigger);
+ return; //not for areas
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.one_way_collision = p_enable;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ Physics2DServer::get_singleton()->body_set_shape_as_one_way_collision(rid, sd.shapes[i].index, p_enable);
+ }
+}
+
+bool CollisionObject2D::is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), false);
+
+ return shapes[p_owner].one_way_collision;
+}
+
+void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) {
+
+ for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ r_owners->push_back(E->key());
+ }
+}
+
+void CollisionObject2D::shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+
+ ShapeData &sd = shapes[p_owner];
+ sd.xform = p_transform;
+ for (int i = 0; i < sd.shapes.size(); i++) {
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_shape_transform(rid, sd.shapes[i].index, p_transform);
+ } else {
+ Physics2DServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
}
}
}
+Transform2D CollisionObject2D::shape_owner_get_transform(uint32_t p_owner) const {
-bool CollisionObject2D::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
+ ERR_FAIL_COND_V(!shapes.has(p_owner), Transform2D());
- if (name.begins_with("shapes/")) {
+ return shapes[p_owner].xform;
+}
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
- if (what == "shape") {
- if (idx >= shapes.size())
- add_shape(RefPtr(p_value));
- else
- set_shape(idx, RefPtr(p_value));
- } else if (what == "transform")
- set_shape_transform(idx, p_value);
- else if (what == "trigger")
- set_shape_as_trigger(idx, p_value);
- } else
- return false;
-
- return true;
+Object *CollisionObject2D::shape_owner_get_owner(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), NULL);
+
+ return shapes[p_owner].owner;
+}
+
+void CollisionObject2D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+ ERR_FAIL_COND(p_shape.is_null());
+
+ ShapeData &sd = shapes[p_owner];
+ ShapeData::Shape s;
+ s.index = total_subshapes;
+ s.shape = p_shape;
+ if (area) {
+ Physics2DServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform);
+ } else {
+ Physics2DServer::get_singleton()->body_add_shape(rid, p_shape->get_rid(), sd.xform);
+ }
+ sd.shapes.push_back(s);
+
+ total_subshapes++;
+}
+int CollisionObject2D::shape_owner_get_shape_count(uint32_t p_owner) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), 0);
+
+ return shapes[p_owner].shapes.size();
+}
+Ref<Shape> CollisionObject2D::shape_owner_get_shape(uint32_t p_owner, int p_shape) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), Ref<Shape>());
+ ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), Ref<Shape>());
+
+ return shapes[p_owner].shapes[p_shape].shape;
+}
+int CollisionObject2D::shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const {
+
+ ERR_FAIL_COND_V(!shapes.has(p_owner), -1);
+ ERR_FAIL_INDEX_V(p_shape, shapes[p_owner].shapes.size(), -1);
+
+ return shapes[p_owner].shapes[p_shape].index;
}
-bool CollisionObject2D::_get(const StringName &p_name, Variant &r_ret) const {
+void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) {
+
+ ERR_FAIL_COND(!shapes.has(p_owner));
+ ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size());
+
+ int index_to_remove = shapes[p_owner].shapes[p_shape].index;
+ if (area) {
+ Physics2DServer::get_singleton()->area_remove_shape(rid, index_to_remove);
+ } else {
+ Physics2DServer::get_singleton()->body_remove_shape(rid, index_to_remove);
+ }
+
+ shapes[p_owner].shapes.remove(p_shape);
+
+ for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().shapes.size(); i++) {
+ if (E->get().shapes[i].index > index_to_remove) {
+ E->get().shapes[i].index -= 1;
+ }
+ }
+ }
- String name = p_name;
+ total_subshapes--;
+}
- if (name.begins_with("shapes/")) {
+void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) {
- int idx = name.get_slicec('/', 1).to_int();
- String what = name.get_slicec('/', 2);
- if (what == "shape")
- r_ret = get_shape(idx);
- else if (what == "transform")
- r_ret = get_shape_transform(idx);
- else if (what == "trigger")
- r_ret = is_shape_set_as_trigger(idx);
- } else
- return false;
+ ERR_FAIL_COND(!shapes.has(p_owner));
- return true;
+ while (shape_owner_get_shape_count(p_owner) > 0) {
+ shape_owner_remove_shape(p_owner, 0);
+ }
}
-void CollisionObject2D::_get_property_list(List<PropertyInfo> *p_list) const {
+uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const {
- //p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR|PROPERTY_USAGE_NO_INSTANCE_STATE) );
+ ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
- for (int i = 0; i < shapes.size(); i++) {
- String path = "shapes/" + itos(i) + "/";
- p_list->push_back(PropertyInfo(Variant::OBJECT, path + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM, path + "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
- p_list->push_back(PropertyInfo(Variant::BOOL, path + "trigger", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_NO_INSTANCE_STATE));
+ for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
+ for (int i = 0; i < E->get().shapes.size(); i++) {
+ if (E->get().shapes[i].index == p_shape_index) {
+ return E->key();
+ }
+ }
}
+
+ //in theory it should be unreachable
+ return 0;
}
void CollisionObject2D::set_pickable(bool p_enabled) {
@@ -216,16 +319,6 @@ void CollisionObject2D::_update_pickable() {
void CollisionObject2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_shape", "shape:Shape2D", "transform"), &CollisionObject2D::add_shape, DEFVAL(Transform2D()));
- ClassDB::bind_method(D_METHOD("get_shape_count"), &CollisionObject2D::get_shape_count);
- ClassDB::bind_method(D_METHOD("set_shape", "shape_idx", "shape:Shape"), &CollisionObject2D::set_shape);
- ClassDB::bind_method(D_METHOD("set_shape_transform", "shape_idx", "transform"), &CollisionObject2D::set_shape_transform);
- ClassDB::bind_method(D_METHOD("set_shape_as_trigger", "shape_idx", "enable"), &CollisionObject2D::set_shape_as_trigger);
- ClassDB::bind_method(D_METHOD("get_shape:Shape2D", "shape_idx"), &CollisionObject2D::get_shape);
- ClassDB::bind_method(D_METHOD("get_shape_transform", "shape_idx"), &CollisionObject2D::get_shape_transform);
- ClassDB::bind_method(D_METHOD("is_shape_set_as_trigger", "shape_idx"), &CollisionObject2D::is_shape_set_as_trigger);
- ClassDB::bind_method(D_METHOD("remove_shape", "shape_idx"), &CollisionObject2D::remove_shape);
- ClassDB::bind_method(D_METHOD("clear_shapes"), &CollisionObject2D::clear_shapes);
ClassDB::bind_method(D_METHOD("get_rid"), &CollisionObject2D::get_rid);
ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
@@ -242,100 +335,13 @@ void CollisionObject2D::_bind_methods() {
ADD_GROUP("", "");
}
-void CollisionObject2D::add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform) {
-
- ERR_FAIL_COND(p_shape.is_null());
-
- ShapeData sdata;
- sdata.shape = p_shape;
- sdata.xform = p_transform;
- sdata.trigger = false;
-
- if (area)
- Physics2DServer::get_singleton()->area_add_shape(get_rid(), p_shape->get_rid(), p_transform);
- else
- Physics2DServer::get_singleton()->body_add_shape(get_rid(), p_shape->get_rid(), p_transform);
-
- shapes.push_back(sdata);
-}
-int CollisionObject2D::get_shape_count() const {
-
- return shapes.size();
-}
-void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- ERR_FAIL_COND(p_shape.is_null());
-
- shapes[p_shape_idx].shape = p_shape;
- if (area)
- Physics2DServer::get_singleton()->area_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
- else
- Physics2DServer::get_singleton()->body_set_shape(get_rid(), p_shape_idx, p_shape->get_rid());
-
- //_update_shapes();
-}
-
-void CollisionObject2D::set_shape_transform(int p_shape_idx, const Transform2D &p_transform) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes[p_shape_idx].xform = p_transform;
-
- if (area)
- Physics2DServer::get_singleton()->area_set_shape_transform(get_rid(), p_shape_idx, p_transform);
- else
- Physics2DServer::get_singleton()->body_set_shape_transform(get_rid(), p_shape_idx, p_transform);
-
- //_update_shapes();
-}
-
-Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Ref<Shape2D>());
- return shapes[p_shape_idx].shape;
-}
-Transform2D CollisionObject2D::get_shape_transform(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), Transform2D());
- return shapes[p_shape_idx].xform;
-}
-void CollisionObject2D::remove_shape(int p_shape_idx) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes.remove(p_shape_idx);
-
- _update_shapes();
-}
-
-void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) {
-
- ERR_FAIL_INDEX(p_shape_idx, shapes.size());
- shapes[p_shape_idx].trigger = p_trigger;
- if (!area && rid.is_valid()) {
-
- Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid, p_shape_idx, p_trigger);
- }
-}
-
-bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const {
-
- ERR_FAIL_INDEX_V(p_shape_idx, shapes.size(), false);
- return shapes[p_shape_idx].trigger;
-}
-
-void CollisionObject2D::clear_shapes() {
-
- shapes.clear();
-
- _update_shapes();
-}
-
CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
rid = p_rid;
area = p_area;
pickable = true;
set_notify_transform(true);
+ total_subshapes = 0;
if (p_area) {
@@ -348,6 +354,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
CollisionObject2D::CollisionObject2D() {
//owner=
+
set_notify_transform(true);
}
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 4d4611afd1..deffe8a002 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -35,37 +35,40 @@
class CollisionObject2D : public Node2D {
- GDCLASS(CollisionObject2D, Node2D);
+ GDCLASS(CollisionObject2D, Node2D)
bool area;
RID rid;
bool pickable;
struct ShapeData {
+
+ Object *owner;
Transform2D xform;
- Ref<Shape2D> shape;
- bool trigger;
+ struct Shape {
+ Ref<Shape2D> shape;
+ int index;
+ };
+
+ Vector<Shape> shapes;
+ bool disabled;
+ bool one_way_collision;
ShapeData() {
- trigger = false;
+ disabled = false;
+ one_way_collision = false;
+ owner = NULL;
}
};
- Vector<ShapeData> shapes;
-
- void _update_shapes();
+ int total_subshapes;
- friend class CollisionShape2D;
- friend class CollisionPolygon2D;
- void _update_shapes_from_children();
+ Map<uint32_t, ShapeData> shapes;
protected:
CollisionObject2D(RID p_rid, bool p_area);
void _notification(int p_what);
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
void _update_pickable();
@@ -75,16 +78,29 @@ protected:
void _mouse_exit();
public:
- void add_shape(const Ref<Shape2D> &p_shape, const Transform2D &p_transform = Transform2D());
- int get_shape_count() const;
- void set_shape(int p_shape_idx, const Ref<Shape2D> &p_shape);
- void set_shape_transform(int p_shape_idx, const Transform2D &p_transform);
- Ref<Shape2D> get_shape(int p_shape_idx) const;
- Transform2D get_shape_transform(int p_shape_idx) const;
- void set_shape_as_trigger(int p_shape_idx, bool p_trigger);
- bool is_shape_set_as_trigger(int p_shape_idx) const;
- void remove_shape(int p_shape_idx);
- void clear_shapes();
+ uint32_t create_shape_owner(Object *p_owner);
+ void remove_shape_owner(uint32_t owner);
+ void get_shape_owners(List<uint32_t> *r_owners);
+
+ void shape_owner_set_transform(uint32_t p_owner, const Transform2D &p_transform);
+ Transform2D shape_owner_get_transform(uint32_t p_owner) const;
+ Object *shape_owner_get_owner(uint32_t p_owner) const;
+
+ void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled);
+ bool is_shape_owner_disabled(uint32_t p_owner) const;
+
+ void shape_owner_set_one_way_collision(uint32_t p_owner, bool p_enable);
+ bool is_shape_owner_one_way_collision_enabled(uint32_t p_owner) const;
+
+ void shape_owner_add_shape(uint32_t p_owner, const Ref<Shape2D> &p_shape);
+ int shape_owner_get_shape_count(uint32_t p_owner) const;
+ Ref<Shape> shape_owner_get_shape(uint32_t p_owner, int p_shape) const;
+ int shape_owner_get_shape_index(uint32_t p_owner, int p_shape) const;
+
+ void shape_owner_remove_shape(uint32_t p_owner, int p_shape);
+ void shape_owner_clear_shapes(uint32_t p_owner);
+
+ uint32_t shape_find_owner(int p_shape_index) const;
void set_pickable(bool p_enabled);
bool is_pickable() const;
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 6b603c6473..bd669eb4c8 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -35,13 +35,9 @@
#include "thirdparty/misc/triangulator.h"
-void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
+void CollisionPolygon2D::_build_polygon() {
- if (unparenting || !can_update_body)
- return;
-
- CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
- ERR_FAIL_COND(!co);
+ parent->shape_owner_clear_shapes(owner_id);
if (polygon.size() == 0)
return;
@@ -53,18 +49,10 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
//here comes the sun, lalalala
//decompose concave into multiple convex polygons and add them
Vector<Vector<Vector2> > decomp = _decompose_in_convex();
- shape_from = co->get_shape_count();
for (int i = 0; i < decomp.size(); i++) {
Ref<ConvexPolygonShape2D> convex = memnew(ConvexPolygonShape2D);
convex->set_points(decomp[i]);
- co->add_shape(convex, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
- }
- shape_to = co->get_shape_count() - 1;
- if (shape_to < shape_from) {
- shape_from = -1;
- shape_to = -1;
+ parent->shape_owner_add_shape(owner_id, convex);
}
} else {
@@ -83,28 +71,8 @@ void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
w = PoolVector<Vector2>::Write();
concave->set_segments(segments);
- co->add_shape(concave, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
-
- shape_from = co->get_shape_count() - 1;
- shape_to = co->get_shape_count() - 1;
+ parent->shape_owner_add_shape(owner_id, concave);
}
-
- //co->add_shape(shape,get_transform());
-}
-
-void CollisionPolygon2D::_update_parent() {
-
- if (!can_update_body)
- return;
- Node *parent = get_parent();
- if (!parent)
- return;
- CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
- if (!co)
- return;
- co->_update_shapes_from_children();
}
Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
@@ -155,33 +123,38 @@ Vector<Vector<Vector2> > CollisionPolygon2D::_decompose_in_convex() {
void CollisionPolygon2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- unparenting = false;
- can_update_body = get_tree()->is_editor_hint();
- if (!get_tree()->is_editor_hint()) {
+ case NOTIFICATION_PARENTED: {
+
+ parent = get_parent()->cast_to<CollisionObject2D>();
+ if (parent) {
+ owner_id = parent->create_shape_owner(this);
+ _build_polygon();
+ parent->shape_owner_set_transform(owner_id, get_transform());
+ parent->shape_owner_set_disabled(owner_id, disabled);
+ parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
+ }
+
+ /*if (get_tree()->is_editor_hint()) {
//display above all else
set_z_as_relative(false);
set_z(VS::CANVAS_ITEM_Z_MAX - 1);
- }
+ }*/
} break;
- case NOTIFICATION_EXIT_TREE: {
- can_update_body = false;
- } break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (!is_inside_tree())
- break;
- if (can_update_body) {
- _update_parent();
- } else if (shape_from >= 0 && shape_to >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- for (int i = shape_from; i <= shape_to; i++) {
- co->set_shape_transform(i, get_transform());
- }
+ if (parent) {
+ parent->shape_owner_set_transform(owner_id, get_transform());
}
} break;
+ case NOTIFICATION_UNPARENTED: {
+ if (parent) {
+ parent->remove_shape_owner(owner_id);
+ }
+ owner_id = 0;
+ parent = NULL;
+ } break;
case NOTIFICATION_DRAW: {
@@ -210,10 +183,22 @@ void CollisionPolygon2D::_notification(int p_what) {
draw_colored_polygon(polygon, get_tree()->get_debug_collisions_color());
#endif
- } break;
- case NOTIFICATION_UNPARENTED: {
- unparenting = true;
- _update_parent();
+ if (one_way_collision) {
+ Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
+ dcol.a = 1.0;
+ Vector2 line_to(0, 20);
+ draw_line(Vector2(), line_to, dcol, 3);
+ Vector<Vector2> pts;
+ float tsize = 8;
+ pts.push_back(line_to + (Vector2(0, tsize)));
+ pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++)
+ cols.push_back(dcol);
+
+ draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ }
} break;
}
}
@@ -222,7 +207,7 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {
polygon = p_polygon;
- if (can_update_body) {
+ {
for (int i = 0; i < polygon.size(); i++) {
if (i == 0)
aabb = Rect2(polygon[i], Size2());
@@ -236,7 +221,10 @@ void CollisionPolygon2D::set_polygon(const Vector<Point2> &p_polygon) {
aabb.position -= aabb.size * 0.3;
aabb.size += aabb.size * 0.6;
}
- _update_parent();
+ }
+
+ if (parent) {
+ _build_polygon();
}
update();
update_configuration_warning();
@@ -251,7 +239,9 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
ERR_FAIL_INDEX(p_mode, 2);
build_mode = p_mode;
- _update_parent();
+ if (parent) {
+ _build_polygon();
+ }
}
CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
@@ -264,79 +254,69 @@ Rect2 CollisionPolygon2D::get_item_rect() const {
return aabb;
}
-void CollisionPolygon2D::set_trigger(bool p_trigger) {
+String CollisionPolygon2D::get_configuration_warning() const {
- trigger = p_trigger;
- _update_parent();
- if (!can_update_body && is_inside_tree() && shape_from >= 0 && shape_to >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- for (int i = shape_from; i <= shape_to; i++) {
- co->set_shape_as_trigger(i, p_trigger);
- }
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
}
-}
-bool CollisionPolygon2D::is_trigger() const {
+ if (polygon.empty()) {
+ return TTR("An empty CollisionPolygon2D has no effect on collision.");
+ }
- return trigger;
+ return String();
}
-void CollisionPolygon2D::_set_shape_range(const Vector2 &p_range) {
-
- shape_from = p_range.x;
- shape_to = p_range.y;
+void CollisionPolygon2D::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+ update();
+ if (parent) {
+ parent->shape_owner_set_disabled(owner_id, p_disabled);
+ }
}
-Vector2 CollisionPolygon2D::_get_shape_range() const {
-
- return Vector2(shape_from, shape_to);
+bool CollisionPolygon2D::is_disabled() const {
+ return disabled;
}
-String CollisionPolygon2D::get_configuration_warning() const {
-
- if (!get_parent()->cast_to<CollisionObject2D>()) {
- return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+void CollisionPolygon2D::set_one_way_collision(bool p_enable) {
+ one_way_collision = p_enable;
+ update();
+ if (parent) {
+ parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+}
- if (polygon.empty()) {
- return TTR("An empty CollisionPolygon2D has no effect on collision.");
- }
+bool CollisionPolygon2D::is_one_way_collision_enabled() const {
- return String();
+ return one_way_collision;
}
void CollisionPolygon2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionPolygon2D::_add_to_collision_object);
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CollisionPolygon2D::set_polygon);
ClassDB::bind_method(D_METHOD("get_polygon"), &CollisionPolygon2D::get_polygon);
ClassDB::bind_method(D_METHOD("set_build_mode", "build_mode"), &CollisionPolygon2D::set_build_mode);
ClassDB::bind_method(D_METHOD("get_build_mode"), &CollisionPolygon2D::get_build_mode);
-
- ClassDB::bind_method(D_METHOD("set_trigger", "trigger"), &CollisionPolygon2D::set_trigger);
- ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionPolygon2D::is_trigger);
-
- ClassDB::bind_method(D_METHOD("_set_shape_range", "shape_range"), &CollisionPolygon2D::_set_shape_range);
- ClassDB::bind_method(D_METHOD("_get_shape_range"), &CollisionPolygon2D::_get_shape_range);
-
- ClassDB::bind_method(D_METHOD("get_collision_object_first_shape"), &CollisionPolygon2D::get_collision_object_first_shape);
- ClassDB::bind_method(D_METHOD("get_collision_object_last_shape"), &CollisionPolygon2D::get_collision_object_last_shape);
+ ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon2D::set_disabled);
+ ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon2D::is_disabled);
+ ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionPolygon2D::set_one_way_collision);
+ ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionPolygon2D::is_one_way_collision_enabled);
ADD_PROPERTY(PropertyInfo(Variant::INT, "build_mode", PROPERTY_HINT_ENUM, "Solids,Segments"), "set_build_mode", "get_build_mode");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shape_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_shape_range", "_get_shape_range");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
}
CollisionPolygon2D::CollisionPolygon2D() {
aabb = Rect2(-10, -10, 20, 20);
build_mode = BUILD_SOLIDS;
- trigger = false;
- unparenting = false;
- shape_from = -1;
- shape_to = -1;
- can_update_body = false;
set_notify_local_transform(true);
+ parent = NULL;
+ owner_id = 0;
+ disabled = false;
+ one_way_collision = false;
}
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index b1a4a4822d..f0666ba9de 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -33,6 +33,8 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class CollisionPolygon2D : public Node2D {
GDCLASS(CollisionPolygon2D, Node2D);
@@ -47,29 +49,20 @@ protected:
Rect2 aabb;
BuildMode build_mode;
Vector<Point2> polygon;
- bool trigger;
- bool unparenting;
-
- void _add_to_collision_object(Object *p_obj);
- void _update_parent();
-
- bool can_update_body;
- int shape_from;
- int shape_to;
-
- void _set_shape_range(const Vector2 &p_range);
- Vector2 _get_shape_range() const;
+ uint32_t owner_id;
+ CollisionObject2D *parent;
+ bool disabled;
+ bool one_way_collision;
Vector<Vector<Vector2> > _decompose_in_convex();
+ void _build_polygon();
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- void set_trigger(bool p_trigger);
- bool is_trigger() const;
-
void set_build_mode(BuildMode p_mode);
BuildMode get_build_mode() const;
@@ -78,11 +71,14 @@ public:
virtual Rect2 get_item_rect() const;
- int get_collision_object_first_shape() const { return shape_from; }
- int get_collision_object_last_shape() const { return shape_to; }
-
virtual String get_configuration_warning() const;
+ void set_disabled(bool p_disabled);
+ bool is_disabled() const;
+
+ void set_one_way_collision(bool p_enable);
+ bool is_one_way_collision_enabled() const;
+
CollisionPolygon2D();
};
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 1687a898db..ff4aa245ec 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -37,68 +37,48 @@
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/shape_line_2d.h"
-void CollisionShape2D::_add_to_collision_object(Object *p_obj) {
-
- if (unparenting)
- return;
-
- CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
- ERR_FAIL_COND(!co);
- update_shape_index = co->get_shape_count();
- co->add_shape(shape, get_transform());
- if (trigger)
- co->set_shape_as_trigger(co->get_shape_count() - 1, true);
-}
-
void CollisionShape2D::_shape_changed() {
update();
- _update_parent();
-}
-
-void CollisionShape2D::_update_parent() {
-
- Node *parent = get_parent();
- if (!parent)
- return;
- CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
- if (!co)
- return;
- co->_update_shapes_from_children();
}
void CollisionShape2D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- unparenting = false;
- can_update_body = get_tree()->is_editor_hint();
- if (!get_tree()->is_editor_hint()) {
+ case NOTIFICATION_PARENTED: {
+
+ parent = get_parent()->cast_to<CollisionObject2D>();
+ if (parent) {
+ owner_id = parent->create_shape_owner(this);
+ if (shape.is_valid()) {
+ parent->shape_owner_add_shape(owner_id, shape);
+ }
+ parent->shape_owner_set_transform(owner_id, get_transform());
+ parent->shape_owner_set_disabled(owner_id, disabled);
+ parent->shape_owner_set_one_way_collision(owner_id, one_way_collision);
+ }
+
+ /*if (get_tree()->is_editor_hint()) {
//display above all else
set_z_as_relative(false);
set_z(VS::CANVAS_ITEM_Z_MAX - 1);
- }
+ }*/
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
- if (!is_inside_tree())
- break;
- if (can_update_body) {
- _update_parent();
- } else if (update_shape_index >= 0) {
-
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape_transform(update_shape_index, get_transform());
- }
+ if (parent) {
+ parent->shape_owner_set_transform(owner_id, get_transform());
}
} break;
- case NOTIFICATION_EXIT_TREE: {
- can_update_body = false;
-
+ case NOTIFICATION_UNPARENTED: {
+ if (parent) {
+ parent->remove_shape_owner(owner_id);
+ }
+ owner_id = 0;
+ parent = NULL;
} break;
/*
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -121,15 +101,33 @@ void CollisionShape2D::_notification(int p_what) {
rect = Rect2();
Color draw_col = get_tree()->get_debug_collisions_color();
+ if (disabled) {
+ float g = draw_col.gray();
+ draw_col.r = g;
+ draw_col.g = g;
+ draw_col.b = g;
+ }
shape->draw(get_canvas_item(), draw_col);
rect = shape->get_rect();
rect = rect.grow(3);
- } break;
- case NOTIFICATION_UNPARENTED: {
- unparenting = true;
- _update_parent();
+ if (one_way_collision) {
+ Color dcol = get_tree()->get_debug_collisions_color(); //0.9,0.2,0.2,0.4);
+ dcol.a = 1.0;
+ Vector2 line_to(0, 20);
+ draw_line(Vector2(), line_to, dcol, 3);
+ Vector<Vector2> pts;
+ float tsize = 8;
+ pts.push_back(line_to + (Vector2(0, tsize)));
+ pts.push_back(line_to + (Vector2(0.707 * tsize, 0)));
+ pts.push_back(line_to + (Vector2(-0.707 * tsize, 0)));
+ Vector<Color> cols;
+ for (int i = 0; i < 3; i++)
+ cols.push_back(dcol);
+
+ draw_primitive(pts, cols, Vector<Vector2>()); //small arrow
+ }
} break;
}
}
@@ -140,14 +138,13 @@ void CollisionShape2D::set_shape(const Ref<Shape2D> &p_shape) {
shape->disconnect("changed", this, "_shape_changed");
shape = p_shape;
update();
- if (is_inside_tree() && can_update_body)
- _update_parent();
- if (is_inside_tree() && !can_update_body && update_shape_index >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape(update_shape_index, p_shape);
+ if (parent) {
+ parent->shape_owner_clear_shapes(owner_id);
+ if (shape.is_valid()) {
+ parent->shape_owner_add_shape(owner_id, shape);
}
}
+
if (shape.is_valid())
shape->connect("changed", this, "_shape_changed");
@@ -164,72 +161,65 @@ Rect2 CollisionShape2D::get_item_rect() const {
return rect;
}
-void CollisionShape2D::set_trigger(bool p_trigger) {
+String CollisionShape2D::get_configuration_warning() const {
- trigger = p_trigger;
- if (can_update_body) {
- _update_parent();
- } else if (is_inside_tree() && update_shape_index >= 0) {
- CollisionObject2D *co = get_parent()->cast_to<CollisionObject2D>();
- if (co) {
- co->set_shape_as_trigger(update_shape_index, p_trigger);
- }
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
}
-}
-bool CollisionShape2D::is_trigger() const {
+ if (!shape.is_valid()) {
+ return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!");
+ }
- return trigger;
+ return String();
}
-void CollisionShape2D::_set_update_shape_index(int p_index) {
-
- update_shape_index = p_index;
+void CollisionShape2D::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+ update();
+ if (parent) {
+ parent->shape_owner_set_disabled(owner_id, p_disabled);
+ }
}
-int CollisionShape2D::_get_update_shape_index() const {
-
- return update_shape_index;
+bool CollisionShape2D::is_disabled() const {
+ return disabled;
}
-String CollisionShape2D::get_configuration_warning() const {
-
- if (!get_parent()->cast_to<CollisionObject2D>()) {
- return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+void CollisionShape2D::set_one_way_collision(bool p_enable) {
+ one_way_collision = p_enable;
+ update();
+ if (parent) {
+ parent->shape_owner_set_one_way_collision(owner_id, p_enable);
}
+}
- if (!shape.is_valid()) {
- return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!");
- }
+bool CollisionShape2D::is_one_way_collision_enabled() const {
- return String();
+ return one_way_collision;
}
void CollisionShape2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &CollisionShape2D::set_shape);
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape2D::get_shape);
+ ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionShape2D::set_disabled);
+ ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape2D::is_disabled);
+ ClassDB::bind_method(D_METHOD("set_one_way_collision", "enabled"), &CollisionShape2D::set_one_way_collision);
+ ClassDB::bind_method(D_METHOD("is_one_way_collision_enabled"), &CollisionShape2D::is_one_way_collision_enabled);
ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape2D::_shape_changed);
- ClassDB::bind_method(D_METHOD("_add_to_collision_object"), &CollisionShape2D::_add_to_collision_object);
- ClassDB::bind_method(D_METHOD("set_trigger", "enable"), &CollisionShape2D::set_trigger);
- ClassDB::bind_method(D_METHOD("is_trigger"), &CollisionShape2D::is_trigger);
-
- ClassDB::bind_method(D_METHOD("_set_update_shape_index", "index"), &CollisionShape2D::_set_update_shape_index);
- ClassDB::bind_method(D_METHOD("_get_update_shape_index"), &CollisionShape2D::_get_update_shape_index);
-
- ClassDB::bind_method(D_METHOD("get_collision_object_shape_index"), &CollisionShape2D::get_collision_object_shape_index);
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trigger"), "set_trigger", "is_trigger");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "_update_shape_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_update_shape_index", "_get_update_shape_index");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "one_way_collision"), "set_one_way_collision", "is_one_way_collision_enabled");
}
CollisionShape2D::CollisionShape2D() {
rect = Rect2(-Point2(10, 10), Point2(20, 20));
set_notify_local_transform(true);
- trigger = false;
- unparenting = false;
- can_update_body = false;
- update_shape_index = -1;
+ owner_id = 0;
+ parent = NULL;
+ disabled = false;
+ one_way_collision = false;
}
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index 3e63981010..1f2b96b91f 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -33,35 +33,33 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
+class CollisionObject2D;
+
class CollisionShape2D : public Node2D {
- GDCLASS(CollisionShape2D, Node2D);
+ GDCLASS(CollisionShape2D, Node2D)
Ref<Shape2D> shape;
Rect2 rect;
- bool trigger;
- bool unparenting;
- bool can_update_body;
+ uint32_t owner_id;
+ CollisionObject2D *parent;
void _shape_changed();
- int update_shape_index;
-
- void _set_update_shape_index(int p_index);
- int _get_update_shape_index() const;
+ bool disabled;
+ bool one_way_collision;
protected:
- void _update_parent();
void _notification(int p_what);
static void _bind_methods();
- void _add_to_collision_object(Object *p_obj);
-
public:
void set_shape(const Ref<Shape2D> &p_shape);
Ref<Shape2D> get_shape() const;
virtual Rect2 get_item_rect() const;
- void set_trigger(bool p_trigger);
- bool is_trigger() const;
- int get_collision_object_shape_index() const { return _get_update_shape_index(); }
+ void set_disabled(bool p_disabled);
+ bool is_disabled() const;
+
+ void set_one_way_collision(bool p_enable);
+ bool is_one_way_collision_enabled() const;
virtual String get_configuration_warning() const;
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 21d64c5d64..beff247264 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -28,903 +28,235 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "particles_2d.h"
-#include "scene/scene_string_names.h"
-
-void ParticleAttractor2D::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_ENTER_TREE: {
-
- _update_owner();
- } break;
- case NOTIFICATION_DRAW: {
-
- if (!get_tree()->is_editor_hint())
- return;
-
- Vector2 pv;
- float dr = MIN(disable_radius, radius);
- for (int i = 0; i <= 32; i++) {
- Vector2 v(Math::sin(i / 32.0 * Math_PI * 2), Math::cos(i / 32.0 * Math_PI * 2));
- if (i > 0) {
- draw_line(pv * radius, v * radius, Color(0, 0, 0.5, 0.9));
- if (dr > 0) {
- draw_line(pv * dr, v * dr, Color(0.5, 0, 0.0, 0.9));
- }
- }
- pv = v;
- }
+#include "scene/3d/particles.h"
+#include "scene/scene_string_names.h"
- } break;
- case NOTIFICATION_EXIT_TREE: {
- if (owner) {
- _set_owner(NULL);
- }
+void Particles2D::set_emitting(bool p_emitting) {
- } break;
- }
+ emitting = p_emitting;
+ VS::get_singleton()->particles_set_emitting(particles, emitting);
}
-void ParticleAttractor2D::_owner_exited() {
+void Particles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(!owner);
- owner->attractors.erase(this);
- owner = NULL;
+ ERR_FAIL_COND(p_amount < 1);
+ amount = p_amount;
+ VS::get_singleton()->particles_set_amount(particles, amount);
}
+void Particles2D::set_lifetime(float p_lifetime) {
-void ParticleAttractor2D::_update_owner() {
-
- if (!is_inside_tree() || !has_node(path)) {
- _set_owner(NULL);
- return;
- }
-
- Node *n = get_node(path);
- ERR_FAIL_COND(!n);
- Particles2D *pn = n->cast_to<Particles2D>();
- if (!pn) {
- _set_owner(NULL);
- return;
- }
-
- _set_owner(pn);
+ ERR_FAIL_COND(p_lifetime <= 0);
+ lifetime = p_lifetime;
+ VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
+void Particles2D::set_pre_process_time(float p_time) {
-void ParticleAttractor2D::_set_owner(Particles2D *p_owner) {
-
- if (owner == p_owner)
- return;
-
- if (owner) {
- owner->disconnect("tree_exited", this, "_owner_exited");
- owner->attractors.erase(this);
- owner = NULL;
- }
- owner = p_owner;
-
- if (owner) {
-
- owner->connect("tree_exited", this, "_owner_exited", varray(), CONNECT_ONESHOT);
- owner->attractors.insert(this);
- }
+ pre_process_time = p_time;
+ VS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time);
}
+void Particles2D::set_explosiveness_ratio(float p_ratio) {
-void ParticleAttractor2D::_bind_methods() {
-
- ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &ParticleAttractor2D::set_enabled);
- ClassDB::bind_method(D_METHOD("is_enabled"), &ParticleAttractor2D::is_enabled);
-
- ClassDB::bind_method(D_METHOD("set_radius", "radius"), &ParticleAttractor2D::set_radius);
- ClassDB::bind_method(D_METHOD("get_radius"), &ParticleAttractor2D::get_radius);
-
- ClassDB::bind_method(D_METHOD("set_disable_radius", "radius"), &ParticleAttractor2D::set_disable_radius);
- ClassDB::bind_method(D_METHOD("get_disable_radius"), &ParticleAttractor2D::get_disable_radius);
-
- ClassDB::bind_method(D_METHOD("set_gravity", "gravity"), &ParticleAttractor2D::set_gravity);
- ClassDB::bind_method(D_METHOD("get_gravity"), &ParticleAttractor2D::get_gravity);
-
- ClassDB::bind_method(D_METHOD("set_absorption", "absorption"), &ParticleAttractor2D::set_absorption);
- ClassDB::bind_method(D_METHOD("get_absorption"), &ParticleAttractor2D::get_absorption);
-
- ClassDB::bind_method(D_METHOD("set_particles_path", "path"), &ParticleAttractor2D::set_particles_path);
- ClassDB::bind_method(D_METHOD("get_particles_path"), &ParticleAttractor2D::get_particles_path);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "disable_radius", PROPERTY_HINT_RANGE, "0.1,16000,0.1"), "set_disable_radius", "get_disable_radius");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-512,512,0.01"), "set_gravity", "get_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "absorption", PROPERTY_HINT_RANGE, "0,512,0.01"), "set_absorption", "get_absorption");
- ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "particles_path", PROPERTY_HINT_RESOURCE_TYPE, "Particles2D"), "set_particles_path", "get_particles_path");
+ explosiveness_ratio = p_ratio;
+ VS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio);
}
+void Particles2D::set_randomness_ratio(float p_ratio) {
-void ParticleAttractor2D::set_enabled(bool p_enabled) {
-
- enabled = p_enabled;
+ randomness_ratio = p_ratio;
+ VS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio);
}
+void Particles2D::set_visibility_rect(const Rect2 &p_aabb) {
-bool ParticleAttractor2D::is_enabled() const {
+ visibility_rect = p_aabb;
+ Rect3 aabb;
+ aabb.position.x = p_aabb.position.x;
+ aabb.position.y = p_aabb.position.y;
+ aabb.size.x = p_aabb.size.x;
+ aabb.size.y = p_aabb.size.y;
- return enabled;
-}
-
-void ParticleAttractor2D::set_radius(float p_radius) {
+ VS::get_singleton()->particles_set_custom_aabb(particles, aabb);
- radius = p_radius;
+ _change_notify("visibility_rect");
update();
}
+void Particles2D::set_use_local_coordinates(bool p_enable) {
-float ParticleAttractor2D::get_radius() const {
-
- return radius;
-}
-
-void ParticleAttractor2D::set_disable_radius(float p_disable_radius) {
-
- disable_radius = p_disable_radius;
- update();
-}
-float ParticleAttractor2D::get_disable_radius() const {
-
- return disable_radius;
-}
-
-void ParticleAttractor2D::set_gravity(float p_gravity) {
-
- gravity = p_gravity;
-}
-float ParticleAttractor2D::get_gravity() const {
-
- return gravity;
-}
-
-void ParticleAttractor2D::set_absorption(float p_absorption) {
-
- absorption = p_absorption;
-}
-float ParticleAttractor2D::get_absorption() const {
-
- return absorption;
-}
-
-void ParticleAttractor2D::set_particles_path(NodePath p_path) {
-
- path = p_path;
- _update_owner();
- update_configuration_warning();
-}
-NodePath ParticleAttractor2D::get_particles_path() const {
-
- return path;
-}
-
-String ParticleAttractor2D::get_configuration_warning() const {
-
- if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) {
- return TTR("Path property must point to a valid Particles2D node to work.");
+ local_coords = p_enable;
+ VS::get_singleton()->particles_set_use_local_coordinates(particles, local_coords);
+ set_notify_transform(!p_enable);
+ if (!p_enable) {
+ _update_particle_emission_transform();
}
-
- return String();
-}
-
-ParticleAttractor2D::ParticleAttractor2D() {
-
- owner = NULL;
- radius = 50;
- disable_radius = 0;
- gravity = 100;
- absorption = 0;
- path = String("..");
- enabled = true;
}
-/****************************************/
+void Particles2D::_update_particle_emission_transform() {
-_FORCE_INLINE_ static float _rand_from_seed(uint64_t *seed) {
+ Transform2D xf2d = get_global_transform();
+ Transform xf;
+ xf.basis.set_axis(0, Vector3(xf2d.get_axis(0).x, xf2d.get_axis(0).y, 0));
+ xf.basis.set_axis(1, Vector3(xf2d.get_axis(1).x, xf2d.get_axis(1).y, 0));
+ xf.set_origin(Vector3(xf2d.get_origin().x, xf2d.get_origin().y, 0));
- uint32_t r = Math::rand_from_seed(seed);
- return 2.0f * (float)r / (float)Math::RANDOM_MAX - 1.0f;
+ VS::get_singleton()->particles_set_emission_transform(particles, xf);
}
-void Particles2D::_process_particles(float p_delta) {
-
- if (particles.size() == 0 || lifetime == 0)
- return;
-
- p_delta *= time_scale;
-
- float frame_time = p_delta;
-
- if (emit_timeout > 0) {
- time_to_live -= frame_time;
- if (time_to_live < 0) {
-
- emitting = false;
- _change_notify("config/emitting");
- };
- };
-
- float next_time = time + frame_time;
-
- if (next_time > lifetime)
- next_time = Math::fmod(next_time, lifetime);
-
- Particle *pdata = &particles[0];
- int particle_count = particles.size();
- Transform2D xform;
- if (!local_space)
- xform = get_global_transform();
+void Particles2D::set_process_material(const Ref<Material> &p_material) {
- active_count = 0;
-
- PoolVector<Point2>::Read r;
- int emission_point_count = 0;
- if (emission_points.size()) {
-
- emission_point_count = emission_points.size();
- r = emission_points.read();
- }
-
- int attractor_count = 0;
- AttractorCache *attractor_ptr = NULL;
-
- if (attractors.size()) {
- if (attractors.size() != attractor_cache.size()) {
- attractor_cache.resize(attractors.size());
- }
-
- int idx = 0;
- Transform2D m;
- if (local_space) {
- m = get_global_transform().affine_inverse();
- }
- for (Set<ParticleAttractor2D *>::Element *E = attractors.front(); E; E = E->next()) {
-
- attractor_cache[idx].pos = m.xform(E->get()->get_global_position());
- attractor_cache[idx].attractor = E->get();
- idx++;
- }
-
- attractor_ptr = attractor_cache.ptr();
- attractor_count = attractor_cache.size();
- }
-
- for (int i = 0; i < particle_count; i++) {
-
- Particle &p = pdata[i];
-
- float restart_time = (i * lifetime / particle_count) * explosiveness;
-
- bool restart = false;
-
- if (next_time < time) {
-
- if (restart_time > time || restart_time < next_time)
- restart = true;
-
- } else if (restart_time > time && restart_time < next_time) {
- restart = true;
- }
-
- if (restart) {
-
- if (emitting) {
-
- p.pos = emissor_offset;
- if (emission_point_count) {
-
- Vector2 ep = r[Math::rand() % emission_point_count];
- if (!local_space) {
- p.pos = xform.xform(p.pos + ep * extents);
- } else {
- p.pos += ep * extents;
- }
- } else {
- if (!local_space) {
- p.pos = xform.xform(p.pos + Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y)));
- } else {
- p.pos += Vector2(Math::random(-extents.x, extents.x), Math::random(-extents.y, extents.y));
- }
- }
- p.seed = Math::rand() % 12345678;
- uint64_t rand_seed = p.seed * (i + 1);
-
- float angle = Math::deg2rad(param[PARAM_DIRECTION] + _rand_from_seed(&rand_seed) * param[PARAM_SPREAD]);
-
- p.velocity = Vector2(Math::sin(angle), Math::cos(angle));
- if (!local_space) {
-
- p.velocity = xform.basis_xform(p.velocity).normalized();
- }
-
- p.velocity *= param[PARAM_LINEAR_VELOCITY] + param[PARAM_LINEAR_VELOCITY] * _rand_from_seed(&rand_seed) * randomness[PARAM_LINEAR_VELOCITY];
- p.velocity += initial_velocity;
- p.active = true;
- p.rot = Math::deg2rad(param[PARAM_INITIAL_ANGLE] + param[PARAM_INITIAL_ANGLE] * randomness[PARAM_INITIAL_ANGLE] * _rand_from_seed(&rand_seed));
- active_count++;
-
- p.frame = Math::fmod(param[PARAM_ANIM_INITIAL_POS] + randomness[PARAM_ANIM_INITIAL_POS] * _rand_from_seed(&rand_seed), 1.0f);
-
- } else {
-
- p.active = false;
- }
-
- } else {
-
- if (!p.active)
- continue;
-
- uint64_t rand_seed = p.seed * (i + 1);
-
- Vector2 force;
-
- //apply gravity
- float gravity_dir = Math::deg2rad(param[PARAM_GRAVITY_DIRECTION] + 180 * randomness[PARAM_GRAVITY_DIRECTION] * _rand_from_seed(&rand_seed));
- force += Vector2(Math::sin(gravity_dir), Math::cos(gravity_dir)) * (param[PARAM_GRAVITY_STRENGTH] + param[PARAM_GRAVITY_STRENGTH] * randomness[PARAM_GRAVITY_STRENGTH] * _rand_from_seed(&rand_seed));
- //apply radial
- Vector2 rvec = (p.pos - emissor_offset).normalized();
- force += rvec * (param[PARAM_RADIAL_ACCEL] + param[PARAM_RADIAL_ACCEL] * randomness[PARAM_RADIAL_ACCEL] * _rand_from_seed(&rand_seed));
- //apply orbit
- float orbitvel = (param[PARAM_ORBIT_VELOCITY] + param[PARAM_ORBIT_VELOCITY] * randomness[PARAM_ORBIT_VELOCITY] * _rand_from_seed(&rand_seed));
- if (orbitvel != 0) {
- Vector2 rel = p.pos - xform.elements[2];
- Transform2D rot(orbitvel * frame_time, Vector2());
- p.pos = rot.xform(rel) + xform.elements[2];
- }
-
- Vector2 tvec = rvec.tangent();
- force += tvec * (param[PARAM_TANGENTIAL_ACCEL] + param[PARAM_TANGENTIAL_ACCEL] * randomness[PARAM_TANGENTIAL_ACCEL] * _rand_from_seed(&rand_seed));
-
- for (int j = 0; j < attractor_count; j++) {
-
- Vector2 vec = (attractor_ptr[j].pos - p.pos);
- float vl = vec.length();
-
- if (!attractor_ptr[j].attractor->enabled || vl == 0 || vl > attractor_ptr[j].attractor->radius)
- continue;
-
- force += vec * attractor_ptr[j].attractor->gravity;
- float fvl = p.velocity.length();
- if (fvl && attractor_ptr[j].attractor->absorption) {
- Vector2 target = vec.normalized();
- p.velocity = p.velocity.normalized().linear_interpolate(target, MIN(frame_time * attractor_ptr[j].attractor->absorption, 1)) * fvl;
- }
-
- if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) {
- p.active = false;
- }
- }
-
- p.velocity += force * frame_time;
-
- if (param[PARAM_DAMPING]) {
- float dmp = param[PARAM_DAMPING] + param[PARAM_DAMPING] * randomness[PARAM_DAMPING] * _rand_from_seed(&rand_seed);
- float v = p.velocity.length();
- v -= dmp * frame_time;
- if (v <= 0) {
- p.velocity = Vector2();
- } else {
- p.velocity = p.velocity.normalized() * v;
- }
- }
-
- p.pos += p.velocity * frame_time;
- p.rot += Math::lerp(param[PARAM_SPIN_VELOCITY], param[PARAM_SPIN_VELOCITY] * randomness[PARAM_SPIN_VELOCITY] * _rand_from_seed(&rand_seed), randomness[PARAM_SPIN_VELOCITY]) * frame_time;
- float anim_spd = param[PARAM_ANIM_SPEED_SCALE] + param[PARAM_ANIM_SPEED_SCALE] * randomness[PARAM_ANIM_SPEED_SCALE] * _rand_from_seed(&rand_seed);
- p.frame = Math::fposmod(p.frame + (frame_time / lifetime) * anim_spd, 1.0f);
-
- active_count++;
- }
+ process_material = p_material;
+ Ref<ParticlesMaterial> pm = p_material;
+ if (pm.is_valid() && !pm->get_flag(ParticlesMaterial::FLAG_DISABLE_Z) && pm->get_gravity() == Vector3(0, -9.8, 0)) {
+ //likely a new material, modify it!
+ pm->set_flag(ParticlesMaterial::FLAG_DISABLE_Z, true);
+ pm->set_gravity(Vector3(0, 98, 0));
}
+ RID material_rid;
+ if (process_material.is_valid())
+ material_rid = process_material->get_rid();
+ VS::get_singleton()->particles_set_process_material(particles, material_rid);
- time = Math::fmod(time + frame_time, lifetime);
- if (!emitting && active_count == 0) {
- emit_signal(SceneStringNames::get_singleton()->emission_finished);
- set_process(false);
- set_fixed_process(false);
- }
-
- update();
+ update_configuration_warning();
}
-void Particles2D::_notification(int p_what) {
-
- switch (p_what) {
-
- case NOTIFICATION_PROCESS: {
-
- _process_particles(get_process_delta_time());
- } break;
-
- case NOTIFICATION_FIXED_PROCESS: {
-
- _process_particles(get_fixed_process_delta_time());
- } break;
-
- case NOTIFICATION_ENTER_TREE: {
-
- float ppt = preprocess;
- while (ppt > 0) {
- _process_particles(0.1);
- ppt -= 0.1;
- }
- } break;
- case NOTIFICATION_DRAW: {
-
- if (particles.size() == 0 || lifetime == 0)
- return;
-
- RID ci = get_canvas_item();
- Size2 size(1, 1);
- Point2 center;
- int total_frames = 1;
+void Particles2D::set_speed_scale(float p_scale) {
- if (!texture.is_null()) {
- size = texture->get_size();
- size.x /= h_frames;
- size.y /= v_frames;
- total_frames = h_frames * v_frames;
- }
-
- float time_pos = (time / lifetime);
-
- Particle *pdata = &particles[0];
- int particle_count = particles.size();
-
- RID texrid;
-
- if (texture.is_valid())
- texrid = texture->get_rid();
-
- Transform2D invxform;
- if (!local_space)
- invxform = get_global_transform().affine_inverse();
-
- int start_particle = (int)(time * (float)particle_count / lifetime);
-
- for (int id = 0; id < particle_count; ++id) {
- int i = start_particle + id;
- if (i >= particle_count) {
- i -= particle_count;
- }
-
- Particle &p = pdata[i];
- if (!p.active)
- continue;
-
- float ptime = ((float)i / particle_count) * explosiveness;
-
- if (ptime < time_pos)
- ptime = time_pos - ptime;
- else
- ptime = (1.0 - ptime) + time_pos;
-
- uint64_t rand_seed = p.seed * (i + 1);
-
- Color color;
-
- if (gradient.is_valid()) {
- color = gradient->get_color_at_offset(ptime);
- } else {
- color = default_color;
- }
-
- {
- float huerand = _rand_from_seed(&rand_seed);
- float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand;
-
- if (Math::abs(huerot) > CMP_EPSILON) {
-
- float h = color.get_h();
- float s = color.get_s();
- float v = color.get_v();
- float a = color.a;
- //float preh=h;
- h += huerot;
- h = Math::abs(Math::fposmod(h, 1.0f));
- //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand));
- //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h));
- color.set_hsv(h, s, v);
- color.a = a;
- }
- }
-
- float initial_size = param[PARAM_INITIAL_SIZE] + param[PARAM_INITIAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_INITIAL_SIZE];
- float final_size = param[PARAM_FINAL_SIZE] + param[PARAM_FINAL_SIZE] * _rand_from_seed(&rand_seed) * randomness[PARAM_FINAL_SIZE];
-
- float size_mult = initial_size * (1.0 - ptime) + final_size * ptime;
-
- //Size2 rectsize=size * size_mult;
- //rectsize=rectsize.floor();
-
- //Rect2 r = Rect2(Vecto,rectsize);
-
- Transform2D xform;
-
- if (p.rot) {
-
- xform.set_rotation(p.rot);
- xform.translate(-size * size_mult / 2.0);
- xform.elements[2] += p.pos;
- } else {
- xform.elements[2] = -size * size_mult / 2.0;
- xform.elements[2] += p.pos;
- }
-
- if (!local_space) {
- xform = invxform * xform;
- }
-
- xform.scale_basis(Size2(size_mult, size_mult));
-
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform);
-
- if (texrid.is_valid()) {
-
- Rect2 src_rect;
- src_rect.size = size;
-
- if (total_frames > 1) {
- int frame = Math::fast_ftoi(Math::floor(p.frame * total_frames)) % total_frames;
- src_rect.position.x = size.x * (frame % h_frames);
- src_rect.position.y = size.y * (frame / h_frames);
- }
- Rect2 dst_rect(Point2(), size);
- if (flip_h)
- dst_rect.size.x = -dst_rect.size.x;
- if (flip_v)
- dst_rect.size.y = -dst_rect.size.y;
-
- texture->draw_rect_region(ci, dst_rect, src_rect, color);
- //VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color);
- } else {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(), size), color);
- }
- }
-
- } break;
- }
-}
-
-static const char *_particlesframe_property_names[Particles2D::PARAM_MAX] = {
- "params/direction",
- "params/spread",
- "params/linear_velocity",
- "params/spin_velocity",
- "params/orbit_velocity",
- "params/gravity_direction",
- "params/gravity_strength",
- "params/radial_accel",
- "params/tangential_accel",
- "params/damping",
- "params/initial_angle",
- "params/initial_size",
- "params/final_size",
- "params/hue_variation",
- "params/anim_speed_scale",
- "params/anim_initial_pos",
-};
-
-static const char *_particlesframe_property_rnames[Particles2D::PARAM_MAX] = {
- "randomness/direction",
- "randomness/spread",
- "randomness/linear_velocity",
- "randomness/spin_velocity",
- "randomness/orbit_velocity",
- "randomness/gravity_direction",
- "randomness/gravity_strength",
- "randomness/radial_accel",
- "randomness/tangential_accel",
- "randomness/damping",
- "randomness/initial_angle",
- "randomness/initial_size",
- "randomness/final_size",
- "randomness/hue_variation",
- "randomness/anim_speed_scale",
- "randomness/anim_initial_pos",
-};
-
-static const char *_particlesframe_property_ranges[Particles2D::PARAM_MAX] = {
- "0,360,0.01",
- "0,180,0.01",
- "-1024,1024,0.01",
- "-1024,1024,0.01",
- "-1024,1024,0.01",
- "0,360,0.01",
- "0,1024,0.01",
- "-128,128,0.01",
- "-128,128,0.01",
- "0,1024,0.001",
- "0,360,0.01",
- "0,1024,0.01",
- "0,1024,0.01",
- "0,1,0.01",
- "0,128,0.01",
- "0,1,0.01",
-};
-
-void Particles2D::set_emitting(bool p_emitting) {
-
- if (emitting == p_emitting)
- return;
-
- if (p_emitting) {
-
- if (active_count == 0)
- time = 0;
- set_process(process_mode == PROCESS_IDLE);
- set_fixed_process(process_mode == PROCESS_FIXED);
- time_to_live = emit_timeout;
- };
- emitting = p_emitting;
- _change_notify("config/emitting");
+ speed_scale = p_scale;
+ VS::get_singleton()->particles_set_speed_scale(particles, p_scale);
}
bool Particles2D::is_emitting() const {
return emitting;
}
-
-void Particles2D::set_process_mode(ProcessMode p_mode) {
-
- process_mode = p_mode;
- const bool should_process = emitting || active_count != 0;
- set_process(should_process && process_mode == PROCESS_IDLE);
- set_fixed_process(should_process && process_mode == PROCESS_FIXED);
-}
-
-Particles2D::ProcessMode Particles2D::get_process_mode() const {
-
- return process_mode;
-}
-
-void Particles2D::set_amount(int p_amount) {
-
- ERR_FAIL_INDEX(p_amount, 1024 + 1);
-
- particles.resize(p_amount);
-}
int Particles2D::get_amount() const {
- return particles.size();
-}
-
-void Particles2D::set_emit_timeout(float p_timeout) {
-
- emit_timeout = p_timeout;
- time_to_live = p_timeout;
-};
-
-float Particles2D::get_emit_timeout() const {
-
- return emit_timeout;
-};
-
-void Particles2D::set_lifetime(float p_lifetime) {
-
- ERR_FAIL_INDEX(p_lifetime, 3600 + 1);
-
- lifetime = p_lifetime;
+ return amount;
}
float Particles2D::get_lifetime() const {
return lifetime;
}
-
-void Particles2D::set_time_scale(float p_time_scale) {
-
- time_scale = p_time_scale;
-}
-float Particles2D::get_time_scale() const {
-
- return time_scale;
-}
-
-void Particles2D::set_pre_process_time(float p_pre_process_time) {
-
- preprocess = p_pre_process_time;
-}
-
float Particles2D::get_pre_process_time() const {
- return preprocess;
-}
-
-void Particles2D::set_param(Parameter p_param, float p_value) {
-
- ERR_FAIL_INDEX(p_param, PARAM_MAX);
- param[p_param] = p_value;
-}
-float Particles2D::get_param(Parameter p_param) const {
-
- ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return param[p_param];
-}
-
-void Particles2D::set_randomness(Parameter p_param, float p_value) {
-
- ERR_FAIL_INDEX(p_param, PARAM_MAX);
- randomness[p_param] = p_value;
-}
-float Particles2D::get_randomness(Parameter p_param) const {
-
- ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
- return randomness[p_param];
-}
-
-void Particles2D::set_texture(const Ref<Texture> &p_texture) {
-
- texture = p_texture;
-}
-
-Ref<Texture> Particles2D::get_texture() const {
-
- return texture;
+ return pre_process_time;
}
+float Particles2D::get_explosiveness_ratio() const {
-void Particles2D::set_color(const Color &p_color) {
-
- default_color = p_color;
+ return explosiveness_ratio;
}
+float Particles2D::get_randomness_ratio() const {
-Color Particles2D::get_color() const {
-
- return default_color;
+ return randomness_ratio;
}
+Rect2 Particles2D::get_visibility_rect() const {
-void Particles2D::set_gradient(const Ref<Gradient> &p_gradient) {
-
- gradient = p_gradient;
+ return visibility_rect;
}
+bool Particles2D::get_use_local_coordinates() const {
-Ref<Gradient> Particles2D::get_gradient() const {
-
- return gradient;
+ return local_coords;
}
+Ref<Material> Particles2D::get_process_material() const {
-void Particles2D::set_emissor_offset(const Point2 &p_offset) {
-
- emissor_offset = p_offset;
+ return process_material;
}
-Point2 Particles2D::get_emissor_offset() const {
+float Particles2D::get_speed_scale() const {
- return emissor_offset;
+ return speed_scale;
}
-void Particles2D::set_use_local_space(bool p_use) {
+void Particles2D::set_draw_order(DrawOrder p_order) {
- local_space = p_use;
+ draw_order = p_order;
+ VS::get_singleton()->particles_set_draw_order(particles, VS::ParticlesDrawOrder(p_order));
}
-bool Particles2D::is_using_local_space() const {
+Particles2D::DrawOrder Particles2D::get_draw_order() const {
- return local_space;
+ return draw_order;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phases(int p_phases) {
-
- //Create color ramp if we have 2 or more phases.
- //Otherwise first phase phase will be assigned to default color.
- if (p_phases > 1 && gradient.is_null()) {
- gradient = Ref<Gradient>(memnew(Gradient()));
- }
- if (gradient.is_valid()) {
- gradient->get_points().resize(p_phases);
- }
+void Particles2D::set_fixed_fps(int p_count) {
+ fixed_fps = p_count;
+ VS::get_singleton()->particles_set_fixed_fps(particles, p_count);
}
-//Deprecated.
-int Particles2D::get_color_phases() const {
-
- if (gradient.is_valid()) {
- return gradient->get_points_count();
- }
- return 0;
+int Particles2D::get_fixed_fps() const {
+ return fixed_fps;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phase_color(int p_phase, const Color &p_color) {
-
- ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES);
- if (gradient.is_valid()) {
- if (gradient->get_points_count() > p_phase)
- gradient->set_color(p_phase, p_color);
- } else {
- if (p_phase == 0)
- default_color = p_color;
- }
+void Particles2D::set_fractional_delta(bool p_enable) {
+ fractional_delta = p_enable;
+ VS::get_singleton()->particles_set_fractional_delta(particles, p_enable);
}
-//Deprecated.
-Color Particles2D::get_color_phase_color(int p_phase) const {
-
- ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, Color());
- if (gradient.is_valid()) {
- return gradient->get_color(p_phase);
- }
- return Color(0, 0, 0, 1);
+bool Particles2D::get_fractional_delta() const {
+ return fractional_delta;
}
-//Deprecated. Converts color phases to color ramp
-void Particles2D::set_color_phase_pos(int p_phase, float p_pos) {
- ERR_FAIL_INDEX(p_phase, MAX_COLOR_PHASES);
- ERR_FAIL_COND(p_pos < 0.0 || p_pos > 1.0);
- if (gradient.is_valid() && gradient->get_points_count() > p_phase) {
- return gradient->set_offset(p_phase, p_pos);
- }
-}
+String Particles2D::get_configuration_warning() const {
-//Deprecated.
-float Particles2D::get_color_phase_pos(int p_phase) const {
+ String warnings;
- ERR_FAIL_INDEX_V(p_phase, MAX_COLOR_PHASES, 0);
- if (gradient.is_valid()) {
- return gradient->get_offset(p_phase);
+ if (process_material.is_null()) {
+ if (warnings != String())
+ warnings += "\n";
+ warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted.");
}
- return 0;
-}
-
-void Particles2D::set_emission_half_extents(const Vector2 &p_extents) {
- extents = p_extents;
+ return warnings;
}
-Vector2 Particles2D::get_emission_half_extents() const {
+Rect2 Particles2D::capture_rect() const {
- return extents;
+ Rect3 aabb = VS::get_singleton()->particles_get_current_aabb(particles);
+ Rect2 r;
+ r.position.x = aabb.position.x;
+ r.position.y = aabb.position.y;
+ r.size.x = aabb.size.x;
+ r.size.y = aabb.size.y;
+ return r;
}
-void Particles2D::set_initial_velocity(const Vector2 &p_velocity) {
-
- initial_velocity = p_velocity;
-}
-Vector2 Particles2D::get_initial_velocity() const {
-
- return initial_velocity;
+void Particles2D::set_texture(const Ref<Texture> &p_texture) {
+ texture = p_texture;
+ update();
}
-void Particles2D::pre_process(float p_delta) {
-
- _process_particles(p_delta);
+Ref<Texture> Particles2D::get_texture() const {
+ return texture;
}
-void Particles2D::set_explosiveness(float p_value) {
+void Particles2D::set_normal_map(const Ref<Texture> &p_normal_map) {
- explosiveness = p_value;
+ normal_map = p_normal_map;
+ update();
}
-float Particles2D::get_explosiveness() const {
-
- return explosiveness;
+Ref<Texture> Particles2D::get_normal_map() const {
+ return normal_map;
}
-void Particles2D::set_flip_h(bool p_flip) {
-
- flip_h = p_flip;
+void Particles2D::_validate_property(PropertyInfo &property) const {
}
-bool Particles2D::is_flipped_h() const {
+void Particles2D::set_v_frames(int p_count) {
- return flip_h;
+ ERR_FAIL_COND(p_count < 1);
+ v_frames = p_count;
+ update();
}
-void Particles2D::set_flip_v(bool p_flip) {
-
- flip_v = p_flip;
-}
-bool Particles2D::is_flipped_v() const {
+int Particles2D::get_v_frames() const {
- return flip_v;
+ return v_frames;
}
-void Particles2D::set_h_frames(int p_frames) {
+void Particles2D::set_h_frames(int p_count) {
- ERR_FAIL_COND(p_frames < 1);
- h_frames = p_frames;
+ ERR_FAIL_COND(p_count < 1);
+ h_frames = p_count;
+ update();
}
int Particles2D::get_h_frames() const {
@@ -932,215 +264,122 @@ int Particles2D::get_h_frames() const {
return h_frames;
}
-void Particles2D::set_v_frames(int p_frames) {
-
- ERR_FAIL_COND(p_frames < 1);
- v_frames = p_frames;
-}
-int Particles2D::get_v_frames() const {
-
- return v_frames;
-}
+void Particles2D::_notification(int p_what) {
-void Particles2D::set_emission_points(const PoolVector<Vector2> &p_points) {
+ if (p_what == NOTIFICATION_DRAW) {
- emission_points = p_points;
-}
+ RID texture_rid;
+ if (texture.is_valid())
+ texture_rid = texture->get_rid();
+ RID normal_rid;
+ if (normal_map.is_valid())
+ normal_rid = texture->get_rid();
-PoolVector<Vector2> Particles2D::get_emission_points() const {
+ VS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid, normal_rid, h_frames, v_frames);
- return emission_points;
-}
+#ifdef TOOLS_ENABLED
+ if (get_tree()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
-void Particles2D::reset() {
+ draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
+ }
+#endif
+ }
- for (int i = 0; i < particles.size(); i++) {
- particles[i].active = false;
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ _update_particle_emission_transform();
}
- time = 0;
- active_count = 0;
}
void Particles2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_emitting", "active"), &Particles2D::set_emitting);
- ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting);
-
- ClassDB::bind_method(D_METHOD("set_process_mode", "mode"), &Particles2D::set_process_mode);
- ClassDB::bind_method(D_METHOD("get_process_mode"), &Particles2D::get_process_mode);
-
+ ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &Particles2D::set_emitting);
ClassDB::bind_method(D_METHOD("set_amount", "amount"), &Particles2D::set_amount);
- ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount);
+ ClassDB::bind_method(D_METHOD("set_lifetime", "secs"), &Particles2D::set_lifetime);
+ ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &Particles2D::set_pre_process_time);
+ ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &Particles2D::set_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &Particles2D::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_visibility_rect", "aabb"), &Particles2D::set_visibility_rect);
+ ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &Particles2D::set_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &Particles2D::set_fixed_fps);
+ ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &Particles2D::set_fractional_delta);
+ ClassDB::bind_method(D_METHOD("set_process_material", "material:Material"), &Particles2D::set_process_material);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &Particles2D::set_speed_scale);
- ClassDB::bind_method(D_METHOD("set_lifetime", "lifetime"), &Particles2D::set_lifetime);
+ ClassDB::bind_method(D_METHOD("is_emitting"), &Particles2D::is_emitting);
+ ClassDB::bind_method(D_METHOD("get_amount"), &Particles2D::get_amount);
ClassDB::bind_method(D_METHOD("get_lifetime"), &Particles2D::get_lifetime);
-
- ClassDB::bind_method(D_METHOD("set_time_scale", "time_scale"), &Particles2D::set_time_scale);
- ClassDB::bind_method(D_METHOD("get_time_scale"), &Particles2D::get_time_scale);
-
- ClassDB::bind_method(D_METHOD("set_pre_process_time", "time"), &Particles2D::set_pre_process_time);
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &Particles2D::get_pre_process_time);
-
- ClassDB::bind_method(D_METHOD("set_emit_timeout", "value"), &Particles2D::set_emit_timeout);
- ClassDB::bind_method(D_METHOD("get_emit_timeout"), &Particles2D::get_emit_timeout);
-
- ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &Particles2D::set_param);
- ClassDB::bind_method(D_METHOD("get_param", "param"), &Particles2D::get_param);
-
- ClassDB::bind_method(D_METHOD("set_randomness", "param", "value"), &Particles2D::set_randomness);
- ClassDB::bind_method(D_METHOD("get_randomness", "param"), &Particles2D::get_randomness);
-
- ClassDB::bind_method(D_METHOD("set_texture:Texture", "texture"), &Particles2D::set_texture);
+ ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &Particles2D::get_explosiveness_ratio);
+ ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &Particles2D::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_visibility_rect"), &Particles2D::get_visibility_rect);
+ ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &Particles2D::get_use_local_coordinates);
+ ClassDB::bind_method(D_METHOD("get_fixed_fps"), &Particles2D::get_fixed_fps);
+ ClassDB::bind_method(D_METHOD("get_fractional_delta"), &Particles2D::get_fractional_delta);
+ ClassDB::bind_method(D_METHOD("get_process_material:Material"), &Particles2D::get_process_material);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &Particles2D::get_speed_scale);
+
+ ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &Particles2D::set_draw_order);
+ ClassDB::bind_method(D_METHOD("get_draw_order"), &Particles2D::get_draw_order);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture:Texture"), &Particles2D::set_texture);
ClassDB::bind_method(D_METHOD("get_texture:Texture"), &Particles2D::get_texture);
- ClassDB::bind_method(D_METHOD("set_color", "color"), &Particles2D::set_color);
- ClassDB::bind_method(D_METHOD("get_color"), &Particles2D::get_color);
-
- ClassDB::bind_method(D_METHOD("set_gradient:Gradient", "gradient"), &Particles2D::set_gradient);
- ClassDB::bind_method(D_METHOD("get_gradient:Gradient"), &Particles2D::get_gradient);
-
- ClassDB::bind_method(D_METHOD("set_emissor_offset", "offset"), &Particles2D::set_emissor_offset);
- ClassDB::bind_method(D_METHOD("get_emissor_offset"), &Particles2D::get_emissor_offset);
-
- ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &Particles2D::set_flip_h);
- ClassDB::bind_method(D_METHOD("is_flipped_h"), &Particles2D::is_flipped_h);
-
- ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &Particles2D::set_flip_v);
- ClassDB::bind_method(D_METHOD("is_flipped_v"), &Particles2D::is_flipped_v);
+ ClassDB::bind_method(D_METHOD("set_normal_map", "texture:Texture"), &Particles2D::set_normal_map);
+ ClassDB::bind_method(D_METHOD("get_normal_map:Texture"), &Particles2D::get_normal_map);
- ClassDB::bind_method(D_METHOD("set_h_frames", "enable"), &Particles2D::set_h_frames);
- ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
+ ClassDB::bind_method(D_METHOD("capture_rect"), &Particles2D::capture_rect);
- ClassDB::bind_method(D_METHOD("set_v_frames", "enable"), &Particles2D::set_v_frames);
+ ClassDB::bind_method(D_METHOD("set_v_frames", "frames"), &Particles2D::set_v_frames);
ClassDB::bind_method(D_METHOD("get_v_frames"), &Particles2D::get_v_frames);
- ClassDB::bind_method(D_METHOD("set_emission_half_extents", "extents"), &Particles2D::set_emission_half_extents);
- ClassDB::bind_method(D_METHOD("get_emission_half_extents"), &Particles2D::get_emission_half_extents);
-
- ClassDB::bind_method(D_METHOD("set_color_phases", "phases"), &Particles2D::set_color_phases);
- ClassDB::bind_method(D_METHOD("get_color_phases"), &Particles2D::get_color_phases);
-
- ClassDB::bind_method(D_METHOD("set_color_phase_color", "phase", "color"), &Particles2D::set_color_phase_color);
- ClassDB::bind_method(D_METHOD("get_color_phase_color", "phase"), &Particles2D::get_color_phase_color);
-
- ClassDB::bind_method(D_METHOD("set_color_phase_pos", "phase", "pos"), &Particles2D::set_color_phase_pos);
- ClassDB::bind_method(D_METHOD("get_color_phase_pos", "phase"), &Particles2D::get_color_phase_pos);
-
- ClassDB::bind_method(D_METHOD("pre_process", "time"), &Particles2D::pre_process);
- ClassDB::bind_method(D_METHOD("reset"), &Particles2D::reset);
-
- ClassDB::bind_method(D_METHOD("set_use_local_space", "enable"), &Particles2D::set_use_local_space);
- ClassDB::bind_method(D_METHOD("is_using_local_space"), &Particles2D::is_using_local_space);
-
- ClassDB::bind_method(D_METHOD("set_initial_velocity", "velocity"), &Particles2D::set_initial_velocity);
- ClassDB::bind_method(D_METHOD("get_initial_velocity"), &Particles2D::get_initial_velocity);
-
- ClassDB::bind_method(D_METHOD("set_explosiveness", "amount"), &Particles2D::set_explosiveness);
- ClassDB::bind_method(D_METHOD("get_explosiveness"), &Particles2D::get_explosiveness);
-
- ClassDB::bind_method(D_METHOD("set_emission_points", "points"), &Particles2D::set_emission_points);
- ClassDB::bind_method(D_METHOD("get_emission_points"), &Particles2D::get_emission_points);
-
- ADD_SIGNAL(MethodInfo("emission_finished"));
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "config/amount", PROPERTY_HINT_EXP_RANGE, "1,1024"), "set_amount", "get_amount");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "config/lifetime", PROPERTY_HINT_EXP_RANGE, "0.1,3600,0.1"), "set_lifetime", "get_lifetime");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/time_scale", PROPERTY_HINT_EXP_RANGE, "0.01,128,0.01"), "set_time_scale", "get_time_scale");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/preprocess", PROPERTY_HINT_EXP_RANGE, "0,3600,0.1"), "set_pre_process_time", "get_pre_process_time");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "config/emit_timeout", PROPERTY_HINT_RANGE, "0,3600,0.1"), "set_emit_timeout", "get_emit_timeout");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "config/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), "set_process_mode", "get_process_mode");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/offset"), "set_emissor_offset", "get_emissor_offset");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "config/half_extents"), "set_emission_half_extents", "get_emission_half_extents");
- ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "config/local_space"), "set_use_local_space", "is_using_local_space");
- ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "config/explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness", "get_explosiveness");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_h"), "set_flip_h", "is_flipped_h");
- ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "config/flip_v"), "set_flip_v", "is_flipped_v");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config/texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/h_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_h_frames", "get_h_frames");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "config/v_frames", PROPERTY_HINT_RANGE, "1,512,1"), "set_v_frames", "get_v_frames");
-
- for (int i = 0; i < PARAM_MAX; i++) {
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, _particlesframe_property_names[i], PROPERTY_HINT_RANGE, _particlesframe_property_ranges[i]), "set_param", "get_param", i);
- }
-
- for (int i = 0; i < PARAM_MAX; i++) {
- ADD_PROPERTYINZ(PropertyInfo(Variant::REAL, _particlesframe_property_rnames[i], PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_randomness", "get_randomness", i);
- }
-
- ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "color_phases/count", PROPERTY_HINT_RANGE, "0,4,1", 0), "set_color_phases", "get_color_phases");
-
- //Backward compatibility. They will be converted to color ramp
- for (int i = 0; i < MAX_COLOR_PHASES; i++) {
- String phase = "phase_" + itos(i) + "/";
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, phase + "pos", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_color_phase_pos", "get_color_phase_pos", i);
- ADD_PROPERTYI(PropertyInfo(Variant::COLOR, phase + "color", PROPERTY_HINT_NONE, "", 0), "set_color_phase_color", "get_color_phase_color", i);
- }
+ ClassDB::bind_method(D_METHOD("set_h_frames", "frames"), &Particles2D::set_h_frames);
+ ClassDB::bind_method(D_METHOD("get_h_frames"), &Particles2D::get_h_frames);
- ADD_PROPERTYNO(PropertyInfo(Variant::COLOR, "color/color"), "set_color", "get_color");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "color/color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
-
- ADD_PROPERTYNZ(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "emission_points", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_emission_points", "get_emission_points");
-
- BIND_CONSTANT(PARAM_DIRECTION);
- BIND_CONSTANT(PARAM_SPREAD);
- BIND_CONSTANT(PARAM_LINEAR_VELOCITY);
- BIND_CONSTANT(PARAM_SPIN_VELOCITY);
- BIND_CONSTANT(PARAM_ORBIT_VELOCITY);
- BIND_CONSTANT(PARAM_GRAVITY_DIRECTION);
- BIND_CONSTANT(PARAM_GRAVITY_STRENGTH);
- BIND_CONSTANT(PARAM_RADIAL_ACCEL);
- BIND_CONSTANT(PARAM_TANGENTIAL_ACCEL);
- BIND_CONSTANT(PARAM_DAMPING);
- BIND_CONSTANT(PARAM_INITIAL_ANGLE);
- BIND_CONSTANT(PARAM_INITIAL_SIZE);
- BIND_CONSTANT(PARAM_FINAL_SIZE);
- BIND_CONSTANT(PARAM_HUE_VARIATION);
- BIND_CONSTANT(PARAM_ANIM_SPEED_SCALE);
- BIND_CONSTANT(PARAM_ANIM_INITIAL_POS);
- BIND_CONSTANT(PARAM_MAX);
-
- BIND_CONSTANT(MAX_COLOR_PHASES);
+ ADD_GROUP("Parameters", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::RECT3, "visibility_rect"), "set_visibility_rect", "get_visibility_rect");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime"), "set_draw_order", "get_draw_order");
+ ADD_GROUP("Process Material", "process_");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material");
+ ADD_GROUP("Textures", "");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "h_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_h_frames", "get_h_frames");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "v_frames", PROPERTY_HINT_RANGE, "1,1024,1"), "set_v_frames", "get_v_frames");
+
+ BIND_CONSTANT(DRAW_ORDER_INDEX);
+ BIND_CONSTANT(DRAW_ORDER_LIFETIME);
}
Particles2D::Particles2D() {
- for (int i = 0; i < PARAM_MAX; i++) {
-
- param[i] = 0;
- randomness[i] = 0;
- }
+ particles = VS::get_singleton()->particles_create();
- set_param(PARAM_SPREAD, 10);
- set_param(PARAM_LINEAR_VELOCITY, 20);
- set_param(PARAM_GRAVITY_STRENGTH, 9.8);
- set_param(PARAM_RADIAL_ACCEL, 0);
- set_param(PARAM_TANGENTIAL_ACCEL, 0);
- set_param(PARAM_INITIAL_ANGLE, 0.0);
- set_param(PARAM_INITIAL_SIZE, 1.0);
- set_param(PARAM_FINAL_SIZE, 1.0);
- set_param(PARAM_ANIM_SPEED_SCALE, 1.0);
-
- set_color(Color(1, 1, 1, 1));
-
- time = 0;
- lifetime = 2;
- emitting = false;
- particles.resize(32);
- active_count = -1;
set_emitting(true);
- process_mode = PROCESS_IDLE;
- local_space = true;
- preprocess = 0;
- time_scale = 1.0;
-
- flip_h = false;
- flip_v = false;
-
- v_frames = 1;
+ set_amount(8);
+ set_lifetime(1);
+ set_fixed_fps(0);
+ set_fractional_delta(true);
+ set_pre_process_time(0);
+ set_explosiveness_ratio(0);
+ set_randomness_ratio(0);
+ set_visibility_rect(Rect2(Vector2(-100, -100), Vector2(200, 200)));
+ set_use_local_coordinates(true);
+ set_speed_scale(1);
h_frames = 1;
+ v_frames = 1;
+}
+
+Particles2D::~Particles2D() {
- emit_timeout = 0;
- time_to_live = 0;
- explosiveness = 1.0;
+ VS::get_singleton()->free(particles);
}
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
index 856beaa836..ab7dcb1464 100644
--- a/scene/2d/particles_2d.h
+++ b/scene/2d/particles_2d.h
@@ -34,235 +34,98 @@
#include "scene/resources/color_ramp.h"
#include "scene/resources/texture.h"
-class Particles2D;
-class ParticleAttractor2D : public Node2D {
-
- GDCLASS(ParticleAttractor2D, Node2D);
-
- friend class Particles2D;
- bool enabled;
- float radius;
- float disable_radius;
- float gravity;
- float absorption;
- NodePath path;
-
- Particles2D *owner;
-
- void _update_owner();
- void _owner_exited();
- void _set_owner(Particles2D *p_owner);
-
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
- void set_enabled(bool p_enabled);
- bool is_enabled() const;
-
- void set_radius(float p_radius);
- float get_radius() const;
-
- void set_disable_radius(float p_disable_radius);
- float get_disable_radius() const;
-
- void set_gravity(float p_gravity);
- float get_gravity() const;
-
- void set_absorption(float p_absorption);
- float get_absorption() const;
-
- void set_particles_path(NodePath p_path);
- NodePath get_particles_path() const;
-
- virtual String get_configuration_warning() const;
-
- ParticleAttractor2D();
-};
-
class Particles2D : public Node2D {
-
- GDCLASS(Particles2D, Node2D);
+private:
+ GDCLASS(Particles2D, Node2D)
public:
- enum Parameter {
- PARAM_DIRECTION,
- PARAM_SPREAD,
- PARAM_LINEAR_VELOCITY,
- PARAM_SPIN_VELOCITY,
- PARAM_ORBIT_VELOCITY,
- PARAM_GRAVITY_DIRECTION,
- PARAM_GRAVITY_STRENGTH,
- PARAM_RADIAL_ACCEL,
- PARAM_TANGENTIAL_ACCEL,
- PARAM_DAMPING,
- PARAM_INITIAL_ANGLE,
- PARAM_INITIAL_SIZE,
- PARAM_FINAL_SIZE,
- PARAM_HUE_VARIATION,
- PARAM_ANIM_SPEED_SCALE,
- PARAM_ANIM_INITIAL_POS,
- PARAM_MAX
- };
-
- enum {
- MAX_COLOR_PHASES = 4
- };
-
- enum ProcessMode {
- PROCESS_FIXED,
- PROCESS_IDLE,
+ enum DrawOrder {
+ DRAW_ORDER_INDEX,
+ DRAW_ORDER_LIFETIME,
};
private:
- float param[PARAM_MAX];
- float randomness[PARAM_MAX];
+ RID particles;
- struct Particle {
- bool active;
- Point2 pos;
- Vector2 velocity;
- float rot;
- float frame;
- uint64_t seed;
- Particle() {
- active = false;
- seed = 123465789;
- rot = 0;
- frame = 0;
- }
- };
-
- Vector<Particle> particles;
-
- struct AttractorCache {
-
- Vector2 pos;
- ParticleAttractor2D *attractor;
- };
-
- Vector<AttractorCache> attractor_cache;
-
- float explosiveness;
- float preprocess;
- float lifetime;
bool emitting;
- bool local_space;
- float emit_timeout;
- float time_to_live;
- float time_scale;
- bool flip_h;
- bool flip_v;
- int h_frames;
+ int amount;
+ float lifetime;
+ float pre_process_time;
+ float explosiveness_ratio;
+ float randomness_ratio;
+ float speed_scale;
+ Rect2 visibility_rect;
+ bool local_coords;
+ int fixed_fps;
+ bool fractional_delta;
int v_frames;
- Point2 emissor_offset;
- Vector2 initial_velocity;
- Vector2 extents;
- PoolVector<Vector2> emission_points;
+ int h_frames;
- ProcessMode process_mode;
+ Ref<Material> process_material;
- float time;
- int active_count;
+ DrawOrder draw_order;
Ref<Texture> texture;
+ Ref<Texture> normal_map;
- //If no color ramp is set then default color is used. Created as simple alternative to color_ramp.
- Color default_color;
- Ref<Gradient> gradient;
-
- void _process_particles(float p_delta);
- friend class ParticleAttractor2D;
-
- Set<ParticleAttractor2D *> attractors;
+ void _update_particle_emission_transform();
protected:
- void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
+ void _notification(int p_what);
public:
void set_emitting(bool p_emitting);
- bool is_emitting() const;
-
- void set_process_mode(ProcessMode p_mode);
- ProcessMode get_process_mode() const;
-
void set_amount(int p_amount);
- int get_amount() const;
-
void set_lifetime(float p_lifetime);
- float get_lifetime() const;
-
- void set_time_scale(float p_time_scale);
- float get_time_scale() const;
+ void set_pre_process_time(float p_time);
+ void set_explosiveness_ratio(float p_ratio);
+ void set_randomness_ratio(float p_ratio);
+ void set_visibility_rect(const Rect2 &p_aabb);
+ void set_use_local_coordinates(bool p_enable);
+ void set_process_material(const Ref<Material> &p_material);
+ void set_speed_scale(float p_scale);
- void set_pre_process_time(float p_pre_process_time);
+ bool is_emitting() const;
+ int get_amount() const;
+ float get_lifetime() const;
float get_pre_process_time() const;
+ float get_explosiveness_ratio() const;
+ float get_randomness_ratio() const;
+ Rect2 get_visibility_rect() const;
+ bool get_use_local_coordinates() const;
+ Ref<Material> get_process_material() const;
+ float get_speed_scale() const;
- void set_emit_timeout(float p_timeout);
- float get_emit_timeout() const;
-
- void set_emission_half_extents(const Vector2 &p_extents);
- Vector2 get_emission_half_extents() const;
+ void set_fixed_fps(int p_count);
+ int get_fixed_fps() const;
- void set_param(Parameter p_param, float p_value);
- float get_param(Parameter p_param) const;
+ void set_fractional_delta(bool p_enable);
+ bool get_fractional_delta() const;
- void set_randomness(Parameter p_randomness, float p_value);
- float get_randomness(Parameter p_randomness) const;
-
- void set_explosiveness(float p_value);
- float get_explosiveness() const;
-
- void set_flip_h(bool p_flip);
- bool is_flipped_h() const;
-
- void set_flip_v(bool p_flip);
- bool is_flipped_v() const;
-
- void set_h_frames(int p_frames);
- int get_h_frames() const;
-
- void set_v_frames(int p_frames);
- int get_v_frames() const;
-
- void set_color_phases(int p_phases);
- int get_color_phases() const;
-
- void set_color_phase_color(int p_phase, const Color &p_color);
- Color get_color_phase_color(int p_phase) const;
-
- void set_color_phase_pos(int p_phase, float p_pos);
- float get_color_phase_pos(int p_phase) const;
+ void set_draw_order(DrawOrder p_order);
+ DrawOrder get_draw_order() const;
void set_texture(const Ref<Texture> &p_texture);
Ref<Texture> get_texture() const;
- void set_color(const Color &p_color);
- Color get_color() const;
-
- void set_gradient(const Ref<Gradient> &p_texture);
- Ref<Gradient> get_gradient() const;
-
- void set_emissor_offset(const Point2 &p_offset);
- Point2 get_emissor_offset() const;
+ void set_normal_map(const Ref<Texture> &p_normal_map);
+ Ref<Texture> get_normal_map() const;
- void set_use_local_space(bool p_use);
- bool is_using_local_space() const;
-
- void set_initial_velocity(const Vector2 &p_velocity);
- Vector2 get_initial_velocity() const;
+ virtual String get_configuration_warning() const;
- void set_emission_points(const PoolVector<Vector2> &p_points);
- PoolVector<Vector2> get_emission_points() const;
+ void set_v_frames(int p_count);
+ int get_v_frames() const;
- void pre_process(float p_delta);
- void reset();
+ void set_h_frames(int p_count);
+ int get_h_frames() const;
+ Rect2 capture_rect() const;
Particles2D();
+ ~Particles2D();
};
-VARIANT_ENUM_CAST(Particles2D::ProcessMode);
-VARIANT_ENUM_CAST(Particles2D::Parameter);
+VARIANT_ENUM_CAST(Particles2D::DrawOrder)
#endif // PARTICLES_FRAME_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 68270ed771..fd261117e1 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -44,28 +44,6 @@ void PhysicsBody2D::_notification(int p_what) {
*/
}
-void PhysicsBody2D::set_one_way_collision_direction(const Vector2 &p_dir) {
-
- one_way_collision_direction = p_dir;
- Physics2DServer::get_singleton()->body_set_one_way_collision_direction(get_rid(), p_dir);
-}
-
-Vector2 PhysicsBody2D::get_one_way_collision_direction() const {
-
- return one_way_collision_direction;
-}
-
-void PhysicsBody2D::set_one_way_collision_max_depth(float p_depth) {
-
- one_way_collision_max_depth = p_depth;
- Physics2DServer::get_singleton()->body_set_one_way_collision_max_depth(get_rid(), p_depth);
-}
-
-float PhysicsBody2D::get_one_way_collision_max_depth() const {
-
- return one_way_collision_max_depth;
-}
-
void PhysicsBody2D::_set_layers(uint32_t p_mask) {
set_collision_layer(p_mask);
@@ -92,10 +70,6 @@ void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody2D::_set_layers);
ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody2D::_get_layers);
- ClassDB::bind_method(D_METHOD("set_one_way_collision_direction", "dir"), &PhysicsBody2D::set_one_way_collision_direction);
- ClassDB::bind_method(D_METHOD("get_one_way_collision_direction"), &PhysicsBody2D::get_one_way_collision_direction);
- ClassDB::bind_method(D_METHOD("set_one_way_collision_max_depth", "depth"), &PhysicsBody2D::set_one_way_collision_max_depth);
- ClassDB::bind_method(D_METHOD("get_one_way_collision_max_depth"), &PhysicsBody2D::get_one_way_collision_max_depth);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body:PhysicsBody2D"), &PhysicsBody2D::remove_collision_exception_with);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_2D_PHYSICS, "", 0), "_set_layers", "_get_layers"); //for backwards compat
@@ -103,9 +77,6 @@ void PhysicsBody2D::_bind_methods() {
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
- ADD_GROUP("", "");
- ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "one_way_collision/direction"), "set_one_way_collision_direction", "get_one_way_collision_direction");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "one_way_collision/max_depth"), "set_one_way_collision_max_depth", "get_one_way_collision_max_depth");
}
void PhysicsBody2D::set_collision_layer(uint32_t p_layer) {
@@ -164,7 +135,6 @@ PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode)
collision_layer = 1;
collision_mask = 1;
- set_one_way_collision_max_depth(0);
set_pickable(false);
}
@@ -971,248 +941,105 @@ RigidBody2D::~RigidBody2D() {
//////////////////////////
-Variant KinematicBody2D::_get_collider() const {
-
- ObjectID oid = get_collider();
- if (oid == 0)
- return Variant();
- Object *obj = ObjectDB::get_instance(oid);
- if (!obj)
- return Variant();
-
- Reference *ref = obj->cast_to<Reference>();
- if (ref) {
- return Ref<Reference>(ref);
- }
-
- return obj;
-}
-
-void KinematicBody2D::revert_motion() {
+Dictionary KinematicBody2D::_move(const Vector2 &p_motion) {
+
+ Collision col;
+ if (move(p_motion, col)) {
+ Dictionary d;
+ d["position"] = col.collision;
+ d["normal"] = col.collision;
+ d["local_shape"] = col.local_shape;
+ d["travel"] = col.travel;
+ d["remainder"] = col.remainder;
+ d["collider_id"] = col.collider;
+ if (col.collider) {
+ d["collider"] = ObjectDB::get_instance(col.collider);
+ } else {
+ d["collider"] = Variant();
+ }
- Transform2D gt = get_global_transform();
- gt.elements[2] -= travel;
- travel = Vector2();
- set_global_transform(gt);
-}
+ d["collider_shape_index"] = col.collider_shape;
+ d["collider_metadata"] = col.collider_metadata;
-Vector2 KinematicBody2D::get_travel() const {
+ return d;
- return travel;
+ } else {
+ return Dictionary();
+ }
}
-Vector2 KinematicBody2D::move(const Vector2 &p_motion) {
-
-#if 1
+bool KinematicBody2D::move(const Vector2 &p_motion, Collision &r_collision) {
Transform2D gt = get_global_transform();
Physics2DServer::MotionResult result;
- colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result);
-
- collider_metadata = result.collider_metadata;
- collider_shape = result.collider_shape;
- collider_vel = result.collider_velocity;
- collision = result.collision_point;
- normal = result.collision_normal;
- collider = result.collider_id;
-
- gt.elements[2] += result.motion;
- set_global_transform(gt);
- travel = result.motion;
-
- return result.remainder;
-
-#else
- //give me back regular physics engine logic
- //this is madness
- //and most people using this function will think
- //what it does is simpler than using physics
- //this took about a week to get right..
- //but is it right? who knows at this point..
-
- colliding = false;
- ERR_FAIL_COND_V(!is_inside_tree(), Vector2());
- Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space());
- ERR_FAIL_COND_V(!dss, Vector2());
- const int max_shapes = 32;
- Vector2 sr[max_shapes * 2];
- int res_shapes;
-
- Set<RID> exclude;
- exclude.insert(get_rid());
-
- //recover first
- int recover_attempts = 4;
-
- bool collided = false;
- uint32_t mask = 0;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY;
- if (true)
- mask |= Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
-
- //print_line("margin: "+rtos(margin));
- do {
-
- //motion recover
- for (int i = 0; i < get_shape_count(); i++) {
-
- if (is_shape_set_as_trigger(i))
- continue;
- if (dss->collide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), Vector2(), margin, sr, max_shapes, res_shapes, exclude, get_layer_mask(), mask))
- collided = true;
- }
-
- if (!collided)
- break;
-
- Vector2 recover_motion;
-
- for (int i = 0; i < res_shapes; i++) {
-
- Vector2 a = sr[i * 2 + 0];
- Vector2 b = sr[i * 2 + 1];
-
- float d = a.distance_to(b);
-
- /*
- if (d<margin)
- continue;
- */
- recover_motion += (b - a) * 0.4;
- }
-
- if (recover_motion == Vector2()) {
- collided = false;
- break;
- }
-
- Transform2D gt = get_global_transform();
- gt.elements[2] += recover_motion;
- set_global_transform(gt);
-
- recover_attempts--;
-
- } while (recover_attempts);
-
- //move second
- float safe = 1.0;
- float unsafe = 1.0;
- int best_shape = -1;
-
- for (int i = 0; i < get_shape_count(); i++) {
-
- if (is_shape_set_as_trigger(i))
- continue;
-
- float lsafe, lunsafe;
- bool valid = dss->cast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion, 0, lsafe, lunsafe, exclude, get_layer_mask(), mask);
- //print_line("shape: "+itos(i)+" travel:"+rtos(ltravel));
- if (!valid) {
-
- safe = 0;
- unsafe = 0;
- best_shape = i; //sadly it's the best
- break;
- }
- if (lsafe == 1.0) {
- continue;
- }
- if (lsafe < safe) {
-
- safe = lsafe;
- unsafe = lunsafe;
- best_shape = i;
- }
+ bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, margin, &result);
+
+ if (colliding) {
+ r_collision.collider_metadata = result.collider_metadata;
+ r_collision.collider_shape = result.collider_shape;
+ r_collision.collider_vel = result.collider_velocity;
+ r_collision.collision = result.collision_point;
+ r_collision.normal = result.collision_normal;
+ r_collision.collider = result.collider_id;
+ r_collision.travel = result.motion;
+ r_collision.remainder = result.remainder;
+ r_collision.local_shape = result.collision_local_shape;
}
- //print_line("best shape: "+itos(best_shape)+" motion "+p_motion);
-
- if (safe >= 1) {
- //not collided
- colliding = false;
-
- } else {
-
- //it collided, let's get the rest info in unsafe advance
- Transform2D ugt = get_global_transform();
- ugt.elements[2] += p_motion * unsafe;
- Physics2DDirectSpaceState::ShapeRestInfo rest_info;
- bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt * get_shape_transform(best_shape), Vector2(), margin, &rest_info, exclude, get_layer_mask(), mask);
- if (!c2) {
- //should not happen, but floating point precision is so weird..
-
- colliding = false;
- } else {
-
- //print_line("Travel: "+rtos(travel));
- colliding = true;
- collision = rest_info.point;
- normal = rest_info.normal;
- collider = rest_info.collider_id;
- collider_vel = rest_info.linear_velocity;
- collider_shape = rest_info.shape;
- collider_metadata = rest_info.metadata;
- }
- }
-
- Vector2 motion = p_motion * safe;
- Transform2D gt = get_global_transform();
- gt.elements[2] += motion;
+ gt.elements[2] += result.motion;
set_global_transform(gt);
- return p_motion - motion;
-#endif
+ return colliding;
}
Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_bounces, float p_floor_max_angle) {
- Vector2 motion = (move_and_slide_floor_velocity + p_linear_velocity) * get_fixed_process_delta_time();
+ Vector2 motion = (floor_velocity + p_linear_velocity) * get_fixed_process_delta_time();
Vector2 lv = p_linear_velocity;
- move_and_slide_on_floor = false;
- move_and_slide_on_ceiling = false;
- move_and_slide_on_wall = false;
- move_and_slide_colliders.clear();
- move_and_slide_floor_velocity = Vector2();
+ on_floor = false;
+ on_ceiling = false;
+ on_wall = false;
+ colliders.clear();
+ floor_velocity = Vector2();
while (p_max_bounces) {
- motion = move(motion);
+ Collision collision;
+
+ bool collided = move(motion, collision);
- if (is_colliding()) {
+ if (collided) {
+
+ motion = collision.remainder;
if (p_floor_direction == Vector2()) {
//all is a wall
- move_and_slide_on_wall = true;
+ on_wall = true;
} else {
- if (get_collision_normal().dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+ if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
- move_and_slide_on_floor = true;
- move_and_slide_floor_velocity = get_collider_velocity();
+ on_floor = true;
+ floor_velocity = collision.collider_vel;
- if (get_travel().length() < 1 && ABS((lv.x - move_and_slide_floor_velocity.x)) < p_slope_stop_min_velocity) {
- revert_motion();
+ if (collision.travel.length() < 1 && ABS((lv.x - floor_velocity.x)) < p_slope_stop_min_velocity) {
+ Transform2D gt = get_global_transform();
+ gt.elements[2] -= collision.travel;
+ set_global_transform(gt);
return Vector2();
}
- } else if (get_collision_normal().dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
- move_and_slide_on_ceiling = true;
+ } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
+ on_ceiling = true;
} else {
- move_and_slide_on_wall = true;
+ on_wall = true;
}
}
- Vector2 n = get_collision_normal();
+ Vector2 n = collision.normal;
motion = motion.slide(n);
lv = lv.slide(n);
- Variant collider = _get_collider();
- if (collider.get_type() != Variant::NIL) {
- move_and_slide_colliders.push_back(collider);
- }
+
+ colliders.push_back(collision);
} else {
break;
@@ -1226,26 +1053,22 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
return lv;
}
-bool KinematicBody2D::is_move_and_slide_on_floor() const {
+bool KinematicBody2D::is_on_floor() const {
- return move_and_slide_on_floor;
+ return on_floor;
}
-bool KinematicBody2D::is_move_and_slide_on_wall() const {
+bool KinematicBody2D::is_on_wall() const {
- return move_and_slide_on_wall;
+ return on_wall;
}
-bool KinematicBody2D::is_move_and_slide_on_ceiling() const {
+bool KinematicBody2D::is_on_ceiling() const {
- return move_and_slide_on_ceiling;
+ return on_ceiling;
}
-Array KinematicBody2D::get_move_and_slide_colliders() const {
- return move_and_slide_colliders;
-}
+Vector2 KinematicBody2D::get_floor_velocity() const {
-Vector2 KinematicBody2D::move_to(const Vector2 &p_position) {
-
- return move(p_position - get_global_position());
+ return floor_velocity;
}
bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion) {
@@ -1255,98 +1078,123 @@ bool KinematicBody2D::test_move(const Transform2D &p_from, const Vector2 &p_moti
return Physics2DServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, margin);
}
-Vector2 KinematicBody2D::get_collision_pos() const {
+void KinematicBody2D::set_safe_margin(float p_margin) {
- ERR_FAIL_COND_V(!colliding, Vector2());
- return collision;
+ margin = p_margin;
}
-Vector2 KinematicBody2D::get_collision_normal() const {
+float KinematicBody2D::get_safe_margin() const {
- ERR_FAIL_COND_V(!colliding, Vector2());
- return normal;
+ return margin;
}
-Vector2 KinematicBody2D::get_collider_velocity() const {
+int KinematicBody2D::get_collision_count() const {
- return collider_vel;
+ return colliders.size();
}
+Vector2 KinematicBody2D::get_collision_position(int p_collision) const {
-ObjectID KinematicBody2D::get_collider() const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
- ERR_FAIL_COND_V(!colliding, 0);
- return collider;
+ return colliders[p_collision].collision;
}
-
-int KinematicBody2D::get_collider_shape() const {
-
- ERR_FAIL_COND_V(!colliding, 0);
- return collider_shape;
+Vector2 KinematicBody2D::get_collision_normal(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].normal;
}
-Variant KinematicBody2D::get_collider_metadata() const {
-
- ERR_FAIL_COND_V(!colliding, 0);
- return collider_metadata;
+Vector2 KinematicBody2D::get_collision_travel(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].travel;
}
-
-bool KinematicBody2D::is_colliding() const {
-
- return colliding;
+Vector2 KinematicBody2D::get_collision_remainder(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].remainder;
}
+Object *KinematicBody2D::get_collision_local_shape(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
+ uint32_t owner = shape_find_owner(colliders[p_collision].local_shape);
+ return shape_owner_get_owner(owner);
+}
+Object *KinematicBody2D::get_collision_collider(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
-void KinematicBody2D::set_collision_margin(float p_margin) {
+ if (colliders[p_collision].collider) {
+ return ObjectDB::get_instance(colliders[p_collision].collider);
+ }
- margin = p_margin;
+ return NULL;
}
+ObjectID KinematicBody2D::get_collision_collider_id(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), 0);
-float KinematicBody2D::get_collision_margin() const {
+ return colliders[p_collision].collider;
+}
+Object *KinematicBody2D::get_collision_collider_shape(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), NULL);
+ Object *collider = get_collision_collider(p_collision);
+ if (collider) {
+ CollisionObject2D *obj2d = collider->cast_to<CollisionObject2D>();
+ if (obj2d) {
+ uint32_t owner = shape_find_owner(colliders[p_collision].collider_shape);
+ return obj2d->shape_owner_get_owner(owner);
+ }
+ }
- return margin;
+ return NULL;
+}
+int KinematicBody2D::get_collision_collider_shape_index(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), -1);
+ return colliders[p_collision].collider_shape;
+}
+Vector2 KinematicBody2D::get_collision_collider_velocity(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Vector2());
+ return colliders[p_collision].collider_vel;
+}
+Variant KinematicBody2D::get_collision_collider_metadata(int p_collision) const {
+ ERR_FAIL_INDEX_V(p_collision, colliders.size(), Variant());
+ return colliders[p_collision].collider_metadata;
}
void KinematicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::move);
- ClassDB::bind_method(D_METHOD("move_to", "position"), &KinematicBody2D::move_to);
+ ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody2D::_move);
ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)));
ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec"), &KinematicBody2D::test_move);
- ClassDB::bind_method(D_METHOD("get_travel"), &KinematicBody2D::get_travel);
- ClassDB::bind_method(D_METHOD("revert_motion"), &KinematicBody2D::revert_motion);
- ClassDB::bind_method(D_METHOD("is_colliding"), &KinematicBody2D::is_colliding);
+ ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody2D::is_on_floor);
+ ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody2D::is_on_ceiling);
+ ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody2D::is_on_wall);
+ ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody2D::get_floor_velocity);
- ClassDB::bind_method(D_METHOD("get_collision_pos"), &KinematicBody2D::get_collision_pos);
- ClassDB::bind_method(D_METHOD("get_collision_normal"), &KinematicBody2D::get_collision_normal);
- ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicBody2D::get_collider_velocity);
- ClassDB::bind_method(D_METHOD("get_collider:Variant"), &KinematicBody2D::_get_collider);
- ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicBody2D::get_collider_shape);
- ClassDB::bind_method(D_METHOD("get_collider_metadata:Variant"), &KinematicBody2D::get_collider_metadata);
- ClassDB::bind_method(D_METHOD("get_move_and_slide_colliders"), &KinematicBody2D::get_move_and_slide_colliders);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_floor"), &KinematicBody2D::is_move_and_slide_on_floor);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_ceiling"), &KinematicBody2D::is_move_and_slide_on_ceiling);
- ClassDB::bind_method(D_METHOD("is_move_and_slide_on_wall"), &KinematicBody2D::is_move_and_slide_on_wall);
+ ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody2D::set_safe_margin);
+ ClassDB::bind_method(D_METHOD("get_safe_margin", "pixels"), &KinematicBody2D::get_safe_margin);
- ClassDB::bind_method(D_METHOD("set_collision_margin", "pixels"), &KinematicBody2D::set_collision_margin);
- ClassDB::bind_method(D_METHOD("get_collision_margin", "pixels"), &KinematicBody2D::get_collision_margin);
+ ClassDB::bind_method(D_METHOD("get_collision_count"), &KinematicBody2D::get_collision_count);
+ ClassDB::bind_method(D_METHOD("get_collision_position", "collision"), &KinematicBody2D::get_collision_position);
+ ClassDB::bind_method(D_METHOD("get_collision_normal", "collision"), &KinematicBody2D::get_collision_normal);
+ ClassDB::bind_method(D_METHOD("get_collision_travel", "collision"), &KinematicBody2D::get_collision_travel);
+ ClassDB::bind_method(D_METHOD("get_collision_remainder", "collision"), &KinematicBody2D::get_collision_remainder);
+ ClassDB::bind_method(D_METHOD("get_collision_local_shape", "collision"), &KinematicBody2D::get_collision_local_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_collider", "collision"), &KinematicBody2D::get_collision_collider);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_id", "collision"), &KinematicBody2D::get_collision_collider_id);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_shape", "collision"), &KinematicBody2D::get_collision_collider_shape);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_shape_index", "collision"), &KinematicBody2D::get_collision_collider_shape_index);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_velocity", "collision"), &KinematicBody2D::get_collision_collider_velocity);
+ ClassDB::bind_method(D_METHOD("get_collision_collider_metadata", "collision"), &KinematicBody2D::get_collision_collider_metadata);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_collision_margin", "get_collision_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
}
KinematicBody2D::KinematicBody2D()
: PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC) {
- colliding = false;
- collider = 0;
-
- collider_shape = 0;
-
margin = 0.08;
- move_and_slide_on_floor = false;
- move_and_slide_on_ceiling = false;
- move_and_slide_on_wall = false;
+ on_floor = false;
+ on_ceiling = false;
+ on_wall = false;
}
KinematicBody2D::~KinematicBody2D() {
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 50c9865f18..8c8e4ebc77 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -40,8 +40,6 @@ class PhysicsBody2D : public CollisionObject2D {
uint32_t collision_layer;
uint32_t collision_mask;
- Vector2 one_way_collision_direction;
- float one_way_collision_max_depth;
void _set_layers(uint32_t p_mask);
uint32_t _get_layers() const;
@@ -68,12 +66,6 @@ public:
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
- void set_one_way_collision_direction(const Vector2 &p_dir);
- Vector2 get_one_way_collision_direction() const;
-
- void set_one_way_collision_max_depth(float p_dir);
- float get_one_way_collision_max_depth() const;
-
PhysicsBody2D();
};
@@ -272,54 +264,60 @@ class KinematicBody2D : public PhysicsBody2D {
GDCLASS(KinematicBody2D, PhysicsBody2D);
+public:
+ struct Collision {
+ Vector2 collision;
+ Vector2 normal;
+ Vector2 collider_vel;
+ ObjectID collider;
+ int collider_shape;
+ Variant collider_metadata;
+ Vector2 remainder;
+ Vector2 travel;
+ int local_shape;
+ };
+
+private:
float margin;
- bool colliding;
- Vector2 collision;
- Vector2 normal;
- Vector2 collider_vel;
- ObjectID collider;
- int collider_shape;
- Variant collider_metadata;
- Vector2 travel;
-
- Vector2 move_and_slide_floor_velocity;
- bool move_and_slide_on_floor;
- bool move_and_slide_on_ceiling;
- bool move_and_slide_on_wall;
- Array move_and_slide_colliders;
-
- Variant _get_collider() const;
+
+ Vector2 floor_velocity;
+ bool on_floor;
+ bool on_ceiling;
+ bool on_wall;
+ Vector<Collision> colliders;
_FORCE_INLINE_ bool _ignores_mode(Physics2DServer::BodyMode) const;
+ Dictionary _move(const Vector2 &p_motion);
+
protected:
static void _bind_methods();
public:
- Vector2 move(const Vector2 &p_motion);
- Vector2 move_to(const Vector2 &p_position);
-
+ bool move(const Vector2 &p_motion, Collision &r_collision);
bool test_move(const Transform2D &p_from, const Vector2 &p_motion);
- bool is_colliding() const;
-
- Vector2 get_travel() const;
- void revert_motion();
-
- Vector2 get_collision_pos() const;
- Vector2 get_collision_normal() const;
- Vector2 get_collider_velocity() const;
- ObjectID get_collider() const;
- int get_collider_shape() const;
- Variant get_collider_metadata() const;
- void set_collision_margin(float p_margin);
- float get_collision_margin() const;
+ void set_safe_margin(float p_margin);
+ float get_safe_margin() const;
Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), float p_slope_stop_min_velocity = 5, int p_max_bounces = 4, float p_floor_max_angle = Math::deg2rad((float)45));
- bool is_move_and_slide_on_floor() const;
- bool is_move_and_slide_on_wall() const;
- bool is_move_and_slide_on_ceiling() const;
- Array get_move_and_slide_colliders() const;
+ bool is_on_floor() const;
+ bool is_on_wall() const;
+ bool is_on_ceiling() const;
+ Vector2 get_floor_velocity() const;
+
+ int get_collision_count() const;
+ Vector2 get_collision_position(int p_collision) const;
+ Vector2 get_collision_normal(int p_collision) const;
+ Vector2 get_collision_travel(int p_collision) const;
+ Vector2 get_collision_remainder(int p_collision) const;
+ Object *get_collision_local_shape(int p_collision) const;
+ Object *get_collision_collider(int p_collision) const;
+ ObjectID get_collision_collider_id(int p_collision) const;
+ Object *get_collision_collider_shape(int p_collision) const;
+ int get_collision_collider_shape_index(int p_collision) const;
+ Vector2 get_collision_collider_velocity(int p_collision) const;
+ Variant get_collision_collider_metadata(int p_collision) const;
KinematicBody2D();
~KinematicBody2D();
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 5b5bce342d..e755b1480b 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -98,7 +98,7 @@ void MeshInstance::_get_property_list(List<PropertyInfo> *p_list) const {
if (mesh.is_valid()) {
for (int i = 0; i < mesh->get_surface_count(); i++) {
- p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "Material"));
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"));
}
}
}
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 71079ea780..722b698b75 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -303,7 +303,7 @@ void Particles::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
ADD_GROUP("Process Material", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ParticlesMaterial,ShaderMaterial"), "set_process_material", "get_process_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material");
ADD_GROUP("Draw Passes", "draw_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_passes", PROPERTY_HINT_RANGE, "0," + itos(MAX_DRAW_PASSES) + ",1"), "set_draw_passes", "get_draw_passes");
for (int i = 0; i < MAX_DRAW_PASSES; i++) {
@@ -404,6 +404,7 @@ void ParticlesMaterial::init_shaders() {
shader_names->emission_texture_point_count = "emission_texture_point_count";
shader_names->emission_texture_points = "emission_texture_points";
shader_names->emission_texture_normal = "emission_texture_normal";
+ shader_names->emission_texture_color = "emission_texture_color";
shader_names->trail_divisor = "trail_divisor";
shader_names->trail_size_modifier = "trail_size_modifier";
@@ -481,6 +482,28 @@ void ParticlesMaterial::_update_shader() {
code += "uniform float anim_speed_random;\n";
code += "uniform float anim_offset_random;\n";
+ switch (emission_shape) {
+ case EMISSION_SHAPE_POINT: {
+ //do none
+ } break;
+ case EMISSION_SHAPE_SPHERE: {
+ code += "uniform float emission_sphere_radius;\n";
+ } break;
+ case EMISSION_SHAPE_BOX: {
+ code += "uniform vec3 emission_box_extents;\n";
+ } break;
+ case EMISSION_SHAPE_DIRECTED_POINTS: {
+ code += "uniform sampler2D emission_texture_normal : hint_black;\n";
+ } //fallthrough
+ case EMISSION_SHAPE_POINTS: {
+ code += "uniform sampler2D emission_texture_points : hint_black;\n";
+ code += "uniform int emission_texture_point_count;\n";
+ if (emission_color_texture.is_valid()) {
+ code += "uniform sampler2D emission_texture_color : hint_white;\n";
+ }
+ } break;
+ }
+
code += "uniform vec4 color_value : hint_color;\n";
code += "uniform int trail_divisor;\n";
@@ -515,25 +538,6 @@ void ParticlesMaterial::_update_shader() {
if (tex_parameters[PARAM_ANIM_OFFSET].is_valid())
code += "uniform sampler2D anim_offset_texture;\n";
- switch (emission_shape) {
- case EMISSION_SHAPE_POINT: {
- //do none
- } break;
- case EMISSION_SHAPE_SPHERE: {
- code += "uniform float emission_sphere_radius;\n";
- } break;
- case EMISSION_SHAPE_BOX: {
- code += "uniform vec3 emission_box_extents;\n";
- } break;
- case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += "uniform sampler2D emission_texture_normal : hint_black;\n";
- } //fallthrough
- case EMISSION_SHAPE_POINTS: {
- code += "uniform sampler2D emission_texture_points : hint_black;\n";
- code += "uniform int emission_texture_point_count;\n";
- } break;
- }
-
if (trail_size_modifier.is_valid()) {
code += "uniform sampler2D trail_size_modifier;\n";
}
@@ -567,7 +571,7 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += " uint base_number=NUMBER/uint(trail_divisor);\n";
- code += " uint alt_seed=hash(base_number+uint(1));\n";
+ code += " uint alt_seed=hash(base_number+uint(1)+RANDOM_SEED);\n";
code += " float angle_rand=rand_from_seed(alt_seed);\n";
code += " float scale_rand=rand_from_seed(alt_seed);\n";
code += " float hue_rot_rand=rand_from_seed(alt_seed);\n";
@@ -576,6 +580,11 @@ void ParticlesMaterial::_update_shader() {
code += "\n";
code += "\n";
code += "\n";
+ if (emission_shape >= EMISSION_SHAPE_POINTS) {
+ code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
+ code += " ivec2 emission_tex_size = textureSize( emission_texture_points, 0 );\n";
+ code += " ivec2 emission_tex_ofs = ivec2( point % emission_tex_size.x, point / emission_tex_size.x );\n";
+ }
code += " if (RESTART) {\n";
if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
@@ -593,11 +602,21 @@ void ParticlesMaterial::_update_shader() {
else
code += " float tex_anim_offset = 0.0;\n";
- 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+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " float angle1 = rand_from_seed(alt_seed)*spread*3.1416;\n";
+ code += " vec3 rot=vec3( cos(angle1), sin(angle1),0.0 );\n";
+ code += " VELOCITY=(rot*initial_linear_velocity+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\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+rot*initial_linear_velocity_random*rand_from_seed(alt_seed));\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.y=0.0;\n"; //phase
@@ -614,21 +633,31 @@ void ParticlesMaterial::_update_shader() {
} break;
case EMISSION_SHAPE_POINTS:
case EMISSION_SHAPE_DIRECTED_POINTS: {
- code += " int point = min(emission_texture_point_count-1,int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n";
- code += " ivec2 tex_size = textureSize( emission_texture_points, 0 );\n";
- code += " ivec2 tex_ofs = ivec2( point % tex_size.x, point / tex_size.x );\n";
- code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, tex_ofs,0).xyz;\n";
+ code += " TRANSFORM[3].xyz = texelFetch(emission_texture_points, emission_tex_ofs,0).xyz;\n";
+
if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS) {
- code += " vec3 normal = texelFetch(emission_texture_normal, tex_ofs,0).xyz;\n";
- code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
- code += " vec3 tangent = normalize(cross(v0, normal));\n";
- code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
- code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " mat2 rotm;";
+ code += " rotm[0]=texelFetch(emission_texture_normal, emission_tex_ofs,0).xy;\n";
+ code += " rotm[1]=rotm[0].yx * vec2(1.0,-1.0);\n";
+ code += " VELOCITY.xy = rotm * VELOCITY.xy;\n";
+ } else {
+ code += " vec3 normal = texelFetch(emission_texture_normal, emission_tex_ofs,0).xyz;\n";
+ code += " vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0, 1.0, 0.0);\n";
+ code += " vec3 tangent = normalize(cross(v0, normal));\n";
+ code += " vec3 bitangent = normalize(cross(tangent, normal));\n";
+ code += " VELOCITY = mat3(tangent,bitangent,normal) * VELOCITY;\n";
+ }
}
} break;
}
code += " VELOCITY = (EMISSION_TRANSFORM * vec4(VELOCITY,0.0)).xyz;\n";
code += " TRANSFORM = EMISSION_TRANSFORM * TRANSFORM;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z=0.0;\n";
+ code += " TRANSFORM[3].z=0.0;\n";
+ }
code += " } else {\n";
@@ -685,6 +714,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec3 force = gravity; \n";
code += " vec3 pos = TRANSFORM[3].xyz; \n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " pos.z=0.0; \n";
+ }
code += " //apply linear acceleration\n";
code += " force+=normalize(VELOCITY) * (linear_accel+tex_linear_accel)*mix(1.0,rand_from_seed(alt_seed),linear_accel_random);\n";
code += " //apply radial acceleration\n";
@@ -693,11 +725,17 @@ void ParticlesMaterial::_update_shader() {
code += " //org=p_transform.origin;\n";
code += " force+=normalize(pos-org) * (radial_accel+tex_radial_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random);\n";
code += " //apply tangential acceleration;\n";
- code += " force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " force+=vec3(normalize((pos-org).yx * vec2(-1.0,1.0)),0.0) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+
+ } else {
+ code += " force+=normalize(cross(normalize(pos-org),normalize(gravity))) * ((tangent_accel+tex_tangent_accel)*mix(1.0,rand_from_seed(alt_seed),radial_accel_random));\n";
+ }
code += " //apply attractor forces\n";
code += " VELOCITY+=force * DELTA;\n";
- if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid())
+ if (tex_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) {
code += " VELOCITY=normalize(VELOCITY)*tex_linear_velocity;\n";
+ }
code += " if (damping+tex_damping>0.0) {\n";
code += " \n";
code += " float v = length(VELOCITY);\n";
@@ -709,9 +747,16 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY=normalize(VELOCITY) * v;\n";
code += " }\n";
code += " }\n";
- code += " float base_angle=(initial_angle+tex_angle)*mix(1.0,angle_rand,initial_angle_random)*3.1416/180.0;\n";
- code += " CUSTOM.x=((base_angle+tex_angle)+CUSTOM.y*LIFETIME*(angular_velocity+tex_angular_velocity)*mix(1.0,rand_from_seed(alt_seed)*2.0-1.0,angular_velocity_random))*3.1416/180.0;\n"; //angle
- code += " CUSTOM.z=(anim_offset+tex_anim_offset)*mix(1.0,anim_offset_rand,anim_offset_random)+CUSTOM.y*LIFETIME*(anim_speed+tex_anim_speed)*mix(1.0,rand_from_seed(alt_seed),anim_speed_random);\n"; //angle
+ 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.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
+
+ } else {
+ code += " CUSTOM.z=clamp(CUSTOM.z,0.0,1.0);\n"; //0 to 1 only
+ }
code += " }\n";
//apply color
//apply hue rotation
@@ -747,28 +792,40 @@ void ParticlesMaterial::_update_shader() {
} else {
code += " COLOR = color_value * hue_rot_mat;\n";
}
+ if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
+ code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
+ }
if (trail_color_modifier.is_valid()) {
code += "if (trail_divisor>1) { COLOR*=textureLod(trail_color_modifier,vec2(float(int(NUMBER)%trail_divisor)/float(trail_divisor-1),0.0),0.0); }\n";
}
code += "\n";
- //orient particle Y towards velocity
- if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
- code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
- code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
- code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
- code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
- code += " } else {\n";
- code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
- code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
- code += " }\n";
+
+ if (flags[FLAG_DISABLE_Z]) {
+
+ code += " TRANSFORM[0]=vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n";
+ code += " TRANSFORM[1]=vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n";
+ code += " TRANSFORM[2]=vec4(0.0,0.0,1.0,0.0);\n";
+
} else {
- code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
- code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
- code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
- }
- //turn particle by rotation in Y
- if (flags[FLAG_ROTATE_Y]) {
- code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
+ //orient particle Y towards velocity
+ if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) {
+ code += " if (length(VELOCITY)>0.0) {TRANSFORM[1].xyz=normalize(VELOCITY);} else {TRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);}\n";
+ code += " if (TRANSFORM[1].xyz==normalize(TRANSFORM[0].xyz)) {\n";
+ code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
+ code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
+ code += " } else {\n";
+ code += "\tTRANSFORM[2].xyz=normalize(cross(normalize(TRANSFORM[0].xyz),normalize(TRANSFORM[1].xyz)));\n";
+ code += "\tTRANSFORM[0].xyz=normalize(cross(normalize(TRANSFORM[1].xyz),normalize(TRANSFORM[2].xyz)));\n";
+ code += " }\n";
+ } else {
+ code += "\tTRANSFORM[0].xyz=normalize(TRANSFORM[0].xyz);\n";
+ code += "\tTRANSFORM[1].xyz=normalize(TRANSFORM[1].xyz);\n";
+ code += "\tTRANSFORM[2].xyz=normalize(TRANSFORM[2].xyz);\n";
+ }
+ //turn particle by rotation in Y
+ if (flags[FLAG_ROTATE_Y]) {
+ code += "\tTRANSFORM = TRANSFORM * mat4( vec4(cos(CUSTOM.x),0.0,-sin(CUSTOM.x),0.0), vec4(0.0,1.0,0.0,0.0),vec4(sin(CUSTOM.x),0.0,cos(CUSTOM.x),0.0),vec4(0.0,0.0,0.0,1.0));\n";
+ }
}
//scale by scale
code += " float base_scale=mix(scale*tex_scale,1.0,scale_random*scale_rand);\n";
@@ -779,6 +836,10 @@ void ParticlesMaterial::_update_shader() {
code += " TRANSFORM[0].xyz*=base_scale;\n";
code += " TRANSFORM[1].xyz*=base_scale;\n";
code += " TRANSFORM[2].xyz*=base_scale;\n";
+ if (flags[FLAG_DISABLE_Z]) {
+ code += " VELOCITY.z=0.0;\n";
+ code += " TRANSFORM[3].z=0.0;\n";
+ }
code += "}\n";
code += "\n";
@@ -1021,16 +1082,9 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_SCALE: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
- Ref<CurveTexture> curve = p_texture;
- if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ Ref<CurveTexture> curve_tex = p_texture;
+ if (curve_tex.is_valid()) {
+ curve_tex->ensure_default_setup();
}
} break;
@@ -1130,6 +1184,16 @@ void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normal
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture);
}
+void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) {
+
+ emission_color_texture = p_colors;
+ RID texture;
+ if (p_colors.is_valid())
+ texture = p_colors->get_rid();
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture);
+ _queue_shader_change();
+}
+
void ParticlesMaterial::set_emission_point_count(int p_count) {
emission_point_count = p_count;
@@ -1158,6 +1222,11 @@ Ref<Texture> ParticlesMaterial::get_emission_normal_texture() const {
return emission_normal_texture;
}
+Ref<Texture> ParticlesMaterial::get_emission_color_texture() const {
+
+ return emission_color_texture;
+}
+
int ParticlesMaterial::get_emission_point_count() const {
return emission_point_count;
@@ -1181,14 +1250,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
Ref<CurveTexture> curve = trail_size_modifier;
if (curve.is_valid()) {
- if (curve->get_min() == 0 && curve->get_max() == 1) {
-
- curve->set_max(32);
- PoolVector<Vector2> points;
- points.push_back(Vector2(0, 1));
- points.push_back(Vector2(1, 1));
- curve->set_points(points);
- }
+ curve->ensure_default_setup();
}
RID texture;
@@ -1247,7 +1309,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const {
property.usage = 0;
}
- if (property.name == "emission_point_texture" && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) {
+ if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) {
property.usage = 0;
}
@@ -1301,6 +1363,9 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_emission_normal_texture", "texture:Texture"), &ParticlesMaterial::set_emission_normal_texture);
ClassDB::bind_method(D_METHOD("get_emission_normal_texture:Texture"), &ParticlesMaterial::get_emission_normal_texture);
+ ClassDB::bind_method(D_METHOD("set_emission_color_texture", "texture:Texture"), &ParticlesMaterial::set_emission_color_texture);
+ ClassDB::bind_method(D_METHOD("get_emission_color_texture:Texture"), &ParticlesMaterial::get_emission_color_texture);
+
ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count);
ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count);
@@ -1326,10 +1391,12 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_point_texture", "get_emission_point_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_normal_texture", "get_emission_normal_texture");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_emission_color_texture", "get_emission_color_texture");
ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count");
ADD_GROUP("Flags", "flag_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_flag", "get_flag", FLAG_ALIGN_Y_TO_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_flag", "get_flag", FLAG_ROTATE_Y);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_flag", "get_flag", FLAG_DISABLE_Z);
ADD_GROUP("Spread", "");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "flatness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_flatness", "get_flatness");
@@ -1379,12 +1446,13 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "anim_loop"), "set_flag", "get_flag", FLAG_ANIM_LOOP);
BIND_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_CONSTANT(PARAM_ANGULAR_VELOCITY);
diff --git a/scene/3d/particles.h b/scene/3d/particles.h
index 63ebd7ed7b..dad8ed4585 100644
--- a/scene/3d/particles.h
+++ b/scene/3d/particles.h
@@ -154,6 +154,8 @@ public:
enum Flags {
FLAG_ALIGN_Y_TO_VELOCITY,
FLAG_ROTATE_Y,
+ FLAG_DISABLE_Z,
+ FLAG_ANIM_LOOP,
FLAG_MAX
};
@@ -171,11 +173,12 @@ private:
struct {
uint32_t texture_mask : 16;
uint32_t texture_color : 1;
- uint32_t flags : 2;
+ uint32_t flags : 4;
uint32_t emission_shape : 2;
uint32_t trail_size_texture : 1;
uint32_t trail_color_texture : 1;
uint32_t invalid_key : 1;
+ uint32_t has_emission_color : 1;
};
uint32_t key;
@@ -213,6 +216,7 @@ private:
mk.emission_shape = emission_shape;
mk.trail_color_texture = trail_color_modifier.is_valid() ? 1 : 0;
mk.trail_size_texture = trail_size_modifier.is_valid() ? 1 : 0;
+ mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
return mk;
}
@@ -269,6 +273,7 @@ private:
StringName emission_texture_point_count;
StringName emission_texture_points;
StringName emission_texture_normal;
+ StringName emission_texture_color;
StringName trail_divisor;
StringName trail_size_modifier;
@@ -302,8 +307,11 @@ private:
Vector3 emission_box_extents;
Ref<Texture> emission_point_texture;
Ref<Texture> emission_normal_texture;
+ Ref<Texture> emission_color_texture;
int emission_point_count;
+ bool anim_loop;
+
int trail_divisor;
Ref<CurveTexture> trail_size_modifier;
@@ -347,6 +355,7 @@ public:
void set_emission_box_extents(Vector3 p_extents);
void set_emission_point_texture(const Ref<Texture> &p_points);
void set_emission_normal_texture(const Ref<Texture> &p_normals);
+ void set_emission_color_texture(const Ref<Texture> &p_colors);
void set_emission_point_count(int p_count);
EmissionShape get_emission_shape() const;
@@ -354,6 +363,7 @@ public:
Vector3 get_emission_box_extents() const;
Ref<Texture> get_emission_point_texture() const;
Ref<Texture> get_emission_normal_texture() const;
+ Ref<Texture> get_emission_color_texture() const;
int get_emission_point_count() const;
void set_trail_divisor(int p_divisor);
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index d535c545a8..5c29918118 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -108,40 +108,58 @@ void PathFollow::_update_transform() {
Vector3 pos = c->interpolate_baked(o, cubic);
Transform t = get_transform();
- if (rotation_mode != ROTATION_NONE) {
-
- Vector3 n = (c->interpolate_baked(o + lookahead, cubic) - pos).normalized();
-
- if (rotation_mode == ROTATION_Y) {
+ t.origin = pos;
+ Vector3 pos_offset = Vector3(h_offset, v_offset, 0);
- n.y = 0;
- n.normalize();
- }
+ if (rotation_mode != ROTATION_NONE) {
+ // perform parallel transport
+ //
+ // see C. Dougan, The Parallel Transport Frame, Game Programming Gems 2 for example
+ // for a discussion about why not Frenet frame.
+
+ Vector3 t_prev = pos - c->interpolate_baked(o - lookahead, cubic);
+ Vector3 t_cur = c->interpolate_baked(o + lookahead, cubic) - pos;
+
+ Vector3 axis = t_prev.cross(t_cur);
+ float dot = t_prev.normalized().dot(t_cur.normalized());
+ float angle = Math::acos(CLAMP(dot, -1, 1));
+
+ if (axis.length() > CMP_EPSILON && angle > CMP_EPSILON) {
+ if (rotation_mode == ROTATION_Y) {
+ // assuming we're referring to global Y-axis. is this correct?
+ axis.x = 0;
+ axis.z = 0;
+ } else if (rotation_mode == ROTATION_XY) {
+ axis.z = 0;
+ } else if (rotation_mode == ROTATION_XYZ) {
+ // all components are OK
+ }
- if (n.length() < CMP_EPSILON) { //nothing, use previous
- n = -t.get_basis().get_axis(2).normalized();
+ t.rotate_basis(axis.normalized(), angle);
}
- Vector3 up = Vector3(0, 1, 0);
-
- if (rotation_mode == ROTATION_XYZ) {
-
- float tilt = c->interpolate_baked_tilt(o);
- if (tilt != 0) {
-
- Basis rot(-n, tilt); //remember.. lookat will be znegative.. znegative!! we abide by opengl clan.
- up = rot.xform(up);
+ // do the additional tilting
+ float tilt_angle = c->interpolate_baked_tilt(o);
+ Vector3 tilt_axis = t_cur; // is this correct??
+
+ if (tilt_axis.length() > CMP_EPSILON && tilt_angle > CMP_EPSILON) {
+ if (rotation_mode == ROTATION_Y) {
+ tilt_axis.x = 0;
+ tilt_axis.z = 0;
+ } else if (rotation_mode == ROTATION_XY) {
+ tilt_axis.z = 0;
+ } else if (rotation_mode == ROTATION_XYZ) {
+ // all components are OK
}
- }
- t.set_look_at(pos, pos + n, up);
+ t.rotate_basis(tilt_axis.normalized(), tilt_angle);
+ }
+ t.translate(pos_offset);
} else {
-
- t.origin = pos;
+ t.origin += pos_offset;
}
- t.origin += t.basis.get_axis(0) * h_offset + t.basis.get_axis(1) * v_offset;
set_transform(t);
}
diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
index 874c21546d..abc7766ecb 100644
--- a/scene/3d/scenario_fx.cpp
+++ b/scene/3d/scenario_fx.cpp
@@ -28,43 +28,44 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "scenario_fx.h"
+#include "scene/main/viewport.h"
void WorldEnvironment::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_WORLD) {
+ if (p_what == Spatial::NOTIFICATION_ENTER_WORLD || p_what == Spatial::NOTIFICATION_ENTER_TREE) {
if (environment.is_valid()) {
- if (get_world()->get_environment().is_valid()) {
+ if (get_viewport()->find_world()->get_environment().is_valid()) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
- get_world()->set_environment(environment);
- add_to_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ get_viewport()->find_world()->set_environment(environment);
+ add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
- } else if (p_what == NOTIFICATION_EXIT_WORLD) {
+ } else if (p_what == Spatial::NOTIFICATION_EXIT_WORLD || p_what == Spatial::NOTIFICATION_EXIT_TREE) {
- if (environment.is_valid() && get_world()->get_environment() == environment) {
- get_world()->set_environment(Ref<Environment>());
- remove_from_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ if (environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) {
+ get_viewport()->find_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
}
}
void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) {
- if (is_inside_world() && environment.is_valid() && get_world()->get_environment() == environment) {
- get_world()->set_environment(Ref<Environment>());
- remove_from_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ if (is_inside_tree() && environment.is_valid() && get_viewport()->find_world()->get_environment() == environment) {
+ get_viewport()->find_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
//clean up
}
environment = p_environment;
- if (is_inside_world() && environment.is_valid()) {
- if (get_world()->get_environment().is_valid()) {
+ if (is_inside_tree() && environment.is_valid()) {
+ if (get_viewport()->find_world()->get_environment().is_valid()) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
- get_world()->set_environment(environment);
- add_to_group("_world_environment_" + itos(get_world()->get_scenario().get_id()));
+ get_viewport()->find_world()->set_environment(environment);
+ add_to_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()));
}
update_configuration_warning();
@@ -77,11 +78,11 @@ Ref<Environment> WorldEnvironment::get_environment() const {
String WorldEnvironment::get_configuration_warning() const {
- if (!is_visible_in_tree() || !is_inside_tree() || !environment.is_valid())
+ if (/*!is_visible_in_tree() ||*/ !is_inside_tree() || !environment.is_valid())
return String();
List<Node *> nodes;
- get_tree()->get_nodes_in_group("_world_environment_" + itos(get_world()->get_scenario().get_id()), &nodes);
+ get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world()->get_scenario().get_id()), &nodes);
if (nodes.size() > 1) {
return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h
index b2a4bc5472..d1e0a63130 100644
--- a/scene/3d/scenario_fx.h
+++ b/scene/3d/scenario_fx.h
@@ -36,9 +36,9 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
-class WorldEnvironment : public Spatial {
+class WorldEnvironment : public Node {
- GDCLASS(WorldEnvironment, Spatial);
+ GDCLASS(WorldEnvironment, Node);
Ref<Environment> environment;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index a95bb4a67f..b6c59ba277 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -273,10 +273,12 @@ void SpriteBase3D::_bind_methods() {
ADD_GROUP("Flags", "");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass"), "set_alpha_cut_mode", "get_alpha_cut_mode");
BIND_CONSTANT(FLAG_TRANSPARENT);
BIND_CONSTANT(FLAG_SHADED);
+ BIND_CONSTANT(FLAG_DOUBLE_SIDED);
BIND_CONSTANT(FLAG_MAX);
BIND_CONSTANT(ALPHA_CUT_DISABLED);
@@ -294,7 +296,7 @@ SpriteBase3D::SpriteBase3D() {
pI = NULL;
for (int i = 0; i < FLAG_MAX; i++)
- flags[i] = i == FLAG_TRANSPARENT;
+ flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED;
axis = Vector3::AXIS_Z;
pixel_size = 0.01;
@@ -387,7 +389,7 @@ void Sprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
- RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
+ RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_TRIANGLE_FAN, texture->get_rid());
@@ -888,7 +890,7 @@ void AnimatedSprite3D::_draw() {
int axis = get_axis();
normal[axis] = 1.0;
- RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
+ RID mat = VS::get_singleton()->material_2d_get(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS);
VS::get_singleton()->immediate_set_material(immediate, mat);
VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_TRIANGLE_FAN, texture->get_rid());
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 625b37c32e..b4600c00b8 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -41,6 +41,7 @@ public:
enum DrawFlags {
FLAG_TRANSPARENT,
FLAG_SHADED,
+ FLAG_DOUBLE_SIDED,
FLAG_MAX
};
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 104fa0f70f..6f8c38eddd 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -316,7 +316,7 @@ void GeometryInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance::get_aabb);
ADD_GROUP("Geometry", "");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material_override", "get_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"), "set_material_override", "get_material_override");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0"), "set_extra_cull_margin", "get_extra_cull_margin");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "visible_in_all_rooms"), "set_flag", "get_flag", FLAG_VISIBLE_IN_ALL_ROOMS);
diff --git a/scene/gui/input_action.cpp b/scene/gui/input_action.cpp
index 311cb4ab13..3f80c31c8b 100644
--- a/scene/gui/input_action.cpp
+++ b/scene/gui/input_action.cpp
@@ -43,7 +43,7 @@ Ref<InputEvent> ShortCut::get_shortcut() const {
bool ShortCut::is_shortcut(const Ref<InputEvent> &p_event) const {
- return shortcut.is_valid() && shortcut->action_match(p_event);
+ return shortcut.is_valid() && shortcut->shortcut_match(p_event);
}
String ShortCut::get_as_text() const {
diff --git a/scene/gui/patch_9_rect.cpp b/scene/gui/patch_9_rect.cpp
index 0c2b94d700..735f36b55d 100644
--- a/scene/gui/patch_9_rect.cpp
+++ b/scene/gui/patch_9_rect.cpp
@@ -44,7 +44,7 @@ void NinePatchRect::_notification(int p_what) {
texture->get_rect_region(rect, src_rect, rect, src_rect);
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, draw_center);
+ VS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[MARGIN_LEFT], margin[MARGIN_TOP]), Vector2(margin[MARGIN_RIGHT], margin[MARGIN_BOTTOM]), VS::NinePatchAxisMode(axis_h), VS::NinePatchAxisMode(axis_v), draw_center);
}
}
@@ -62,6 +62,10 @@ void NinePatchRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_region_rect"), &NinePatchRect::get_region_rect);
ClassDB::bind_method(D_METHOD("set_draw_center", "draw_center"), &NinePatchRect::set_draw_center);
ClassDB::bind_method(D_METHOD("get_draw_center"), &NinePatchRect::get_draw_center);
+ ClassDB::bind_method(D_METHOD("set_h_axis_stretch_mode", "mode"), &NinePatchRect::set_h_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_h_axis_stretch_mode"), &NinePatchRect::get_h_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("set_v_axis_stretch_mode", "mode"), &NinePatchRect::set_v_axis_stretch_mode);
+ ClassDB::bind_method(D_METHOD("get_v_axis_stretch_mode"), &NinePatchRect::get_v_axis_stretch_mode);
ADD_SIGNAL(MethodInfo("texture_changed"));
@@ -74,6 +78,13 @@ void NinePatchRect::_bind_methods() {
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_TOP);
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_RIGHT);
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "patch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1"), "set_patch_margin", "get_patch_margin", MARGIN_BOTTOM);
+ ADD_GROUP("Axis Stretch", "axis_stretch_");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_horizontal", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_h_axis_stretch_mode", "get_h_axis_stretch_mode");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "axis_stretch_vertical", PROPERTY_HINT_ENUM, "Stretch,Tile,Tile Fit"), "set_v_axis_stretch_mode", "get_v_axis_stretch_mode");
+
+ BIND_CONSTANT(AXIS_STRETCH_MODE_STRETCH);
+ BIND_CONSTANT(AXIS_STRETCH_MODE_TILE);
+ BIND_CONSTANT(AXIS_STRETCH_MODE_TILE_FIT);
}
void NinePatchRect::set_texture(const Ref<Texture> &p_tex) {
@@ -150,6 +161,26 @@ bool NinePatchRect::get_draw_center() const {
return draw_center;
}
+void NinePatchRect::set_h_axis_stretch_mode(AxisStretchMode p_mode) {
+ axis_h = p_mode;
+ update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_h_axis_stretch_mode() const {
+ return axis_h;
+}
+
+void NinePatchRect::set_v_axis_stretch_mode(AxisStretchMode p_mode) {
+
+ axis_v = p_mode;
+ update();
+}
+
+NinePatchRect::AxisStretchMode NinePatchRect::get_v_axis_stretch_mode() const {
+
+ return axis_v;
+}
+
NinePatchRect::NinePatchRect() {
margin[MARGIN_LEFT] = 0;
@@ -159,6 +190,9 @@ NinePatchRect::NinePatchRect() {
set_mouse_filter(MOUSE_FILTER_IGNORE);
draw_center = true;
+
+ axis_h = AXIS_STRETCH_MODE_STRETCH;
+ axis_v = AXIS_STRETCH_MODE_STRETCH;
}
NinePatchRect::~NinePatchRect() {
diff --git a/scene/gui/patch_9_rect.h b/scene/gui/patch_9_rect.h
index ba978f2f81..602a6d22bf 100644
--- a/scene/gui/patch_9_rect.h
+++ b/scene/gui/patch_9_rect.h
@@ -38,11 +38,20 @@ class NinePatchRect : public Control {
GDCLASS(NinePatchRect, Control);
+public:
+ enum AxisStretchMode {
+ AXIS_STRETCH_MODE_STRETCH,
+ AXIS_STRETCH_MODE_TILE,
+ AXIS_STRETCH_MODE_TILE_FIT,
+ };
+
bool draw_center;
int margin[4];
Rect2 region_rect;
Ref<Texture> texture;
+ AxisStretchMode axis_h, axis_v;
+
protected:
void _notification(int p_what);
virtual Size2 get_minimum_size() const;
@@ -61,7 +70,15 @@ public:
void set_draw_center(bool p_enable);
bool get_draw_center() const;
+ void set_h_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_h_axis_stretch_mode() const;
+
+ void set_v_axis_stretch_mode(AxisStretchMode p_mode);
+ AxisStretchMode get_v_axis_stretch_mode() const;
+
NinePatchRect();
~NinePatchRect();
};
+
+VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode)
#endif // PATCH_9_FRAME_H
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index c4991700aa..74b26da580 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -884,7 +884,7 @@ void PopupMenu::activate_item(int p_item) {
while (pop) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if (hide_on_item_selection && pop->is_hide_on_item_selection()) {
+ 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 = next->cast_to<PopupMenu>();
@@ -895,8 +895,8 @@ void PopupMenu::activate_item(int p_item) {
}
}
// Hides popup by default; unless otherwise specified
- // by using set_hide_on_item_selection
- if (hide_on_item_selection) {
+ // 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();
}
}
@@ -1019,6 +1019,16 @@ bool PopupMenu::is_hide_on_item_selection() {
return hide_on_item_selection;
}
+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() {
+
+ return hide_on_checkable_item_selection;
+}
+
String PopupMenu::get_tooltip(const Point2 &p_pos) const {
int over = _get_mouse_over(p_pos);
@@ -1107,10 +1117,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hide_on_item_selection", "enable"), &PopupMenu::set_hide_on_item_selection);
ClassDB::bind_method(D_METHOD("is_hide_on_item_selection"), &PopupMenu::is_hide_on_item_selection);
+ 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("_submenu_timeout"), &PopupMenu::_submenu_timeout);
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_items", "_get_items");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_item_selection"), "set_hide_on_item_selection", "is_hide_on_item_selection");
+ ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_checkable_item_selection"), "set_hide_on_checkable_item_selection", "is_hide_on_checkable_item_selection");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID")));
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
@@ -1128,6 +1142,7 @@ PopupMenu::PopupMenu() {
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
set_hide_on_item_selection(true);
+ set_hide_on_checkable_item_selection(true);
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 7ef532453d..a9bd8f7e50 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -85,6 +85,7 @@ class PopupMenu : public Popup {
bool invalidated_click;
bool hide_on_item_selection;
+ bool hide_on_checkable_item_selection;
Vector2 moved;
Array _get_items() const;
@@ -168,6 +169,9 @@ public:
void set_hide_on_item_selection(bool p_enabled);
bool is_hide_on_item_selection();
+ void set_hide_on_checkable_item_selection(bool p_enabled);
+ bool is_hide_on_checkable_item_selection();
+
PopupMenu();
~PopupMenu();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 0b8595de42..78ede6e494 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1831,7 +1831,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_image", "image:Texture"), &RichTextLabel::add_image);
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line"), &RichTextLabel::remove_line);
- ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
+ ClassDB::bind_method(D_METHOD("push_font", "font:Font"), &RichTextLabel::push_font);
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
ClassDB::bind_method(D_METHOD("push_align", "align"), &RichTextLabel::push_align);
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
@@ -1854,7 +1854,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll:VScrollBar"), &RichTextLabel::get_v_scroll);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 714327c5b7..a87c83f17c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -49,6 +49,7 @@
#include "scene/scene_string_names.h"
#include "global_config.h"
+#include "scene/3d/scenario_fx.h"
void ViewportTexture::setup_local_to_scene() {
@@ -1017,10 +1018,9 @@ void Viewport::_propagate_enter_world(Node *p_node) {
if (!p_node->is_inside_tree()) //may not have entered scene yet
return;
- Spatial *s = p_node->cast_to<Spatial>();
- if (s) {
+ if (p_node->cast_to<Spatial>() || p_node->cast_to<WorldEnvironment>()) {
- s->notification(Spatial::NOTIFICATION_ENTER_WORLD);
+ p_node->notification(Spatial::NOTIFICATION_ENTER_WORLD);
} else {
Viewport *v = p_node->cast_to<Viewport>();
if (v) {
@@ -1055,10 +1055,9 @@ void Viewport::_propagate_exit_world(Node *p_node) {
if (!p_node->is_inside_tree()) //may have exited scene already
return;
- Spatial *s = p_node->cast_to<Spatial>();
- if (s) {
+ if (p_node->cast_to<Spatial>() || p_node->cast_to<WorldEnvironment>()) {
- s->notification(Spatial::NOTIFICATION_EXIT_WORLD, true);
+ p_node->notification(Spatial::NOTIFICATION_EXIT_WORLD);
} else {
Viewport *v = p_node->cast_to<Viewport>();
if (v) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index b2737353fb..151bc80321 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -469,9 +469,12 @@ void register_scene_types() {
ClassDB::register_class<Shader>();
ClassDB::register_class<ShaderMaterial>();
ClassDB::register_virtual_class<CanvasItem>();
+ ClassDB::register_class<CanvasItemMaterial>();
+ SceneTree::add_idle_callback(CanvasItemMaterial::flush_changes);
+ CanvasItemMaterial::init_shaders();
ClassDB::register_class<Node2D>();
ClassDB::register_class<Particles2D>();
- ClassDB::register_class<ParticleAttractor2D>();
+ //ClassDB::register_class<ParticleAttractor2D>();
ClassDB::register_class<Sprite>();
//ClassDB::register_type<ViewportSprite>();
ClassDB::register_class<SpriteFrames>();
@@ -577,6 +580,7 @@ void register_scene_types() {
ClassDB::register_class<Animation>();
ClassDB::register_virtual_class<Font>();
ClassDB::register_class<BitmapFont>();
+ ClassDB::register_class<Curve>();
ClassDB::register_class<DynamicFontData>();
ClassDB::register_class<DynamicFont>();
@@ -663,5 +667,6 @@ void unregister_scene_types() {
SpatialMaterial::finish_shaders();
ParticlesMaterial::finish_shaders();
+ CanvasItemMaterial::finish_shaders();
SceneStringNames::free();
}
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 10c12c9411..006e7de562 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -380,6 +380,365 @@ Curve2D::Curve2D()
#endif
+Curve::Curve() {
+ _bake_resolution = 100;
+ _baked_cache_dirty = false;
+#ifdef TOOLS_ENABLED
+ _disable_set_data = false;
+#endif
+}
+
+int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent) {
+ // Add a point and preserve order
+
+ // Curve bounds is in 0..1
+ if (p_pos.x > MAX_X)
+ p_pos.x = MAX_X;
+ else if (p_pos.x < MIN_X)
+ p_pos.x = MIN_X;
+
+ int ret = -1;
+
+ if (_points.size() == 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+
+ } else if (_points.size() == 1) {
+ // TODO Is the `else` able to handle this block already?
+
+ real_t diff = p_pos.x - _points[0].pos.x;
+
+ if (diff > 0) {
+ _points.push_back(Point(p_pos, left_tangent, right_tangent));
+ ret = 1;
+ } else {
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ }
+
+ } else {
+
+ int i = get_index(p_pos.x);
+
+ int nearest_index = i;
+ if (i + 1 < _points.size()) {
+ real_t diff0 = p_pos.x - _points[i].pos.x;
+ real_t diff1 = _points[i + 1].pos.x - p_pos.x;
+
+ if (diff1 < diff0)
+ nearest_index = i + 1;
+ }
+
+ if (i == 0 && p_pos.x < _points[0].pos.x) {
+ // Insert before anything else
+ _points.insert(0, Point(p_pos, left_tangent, right_tangent));
+ ret = 0;
+ } else {
+ // Insert between i and i+1
+ ++i;
+ _points.insert(i, Point(p_pos, left_tangent, right_tangent));
+ ret = i;
+ }
+ }
+
+ mark_dirty();
+
+ return ret;
+}
+
+int Curve::get_index(real_t offset) const {
+
+ // Lower-bound float binary search
+
+ int imin = 0;
+ int imax = _points.size() - 1;
+
+ while (imax - imin > 1) {
+ int m = (imin + imax) / 2;
+
+ real_t a = _points[m].pos.x;
+ real_t b = _points[m + 1].pos.x;
+
+ if (a < offset && b < offset) {
+ imin = m;
+
+ } else if (a > offset) {
+ imax = m;
+
+ } else {
+ return m;
+ }
+ }
+
+ // Will happen if the offset is out of bounds
+ if (offset > _points[imax].pos.x)
+ return imax;
+ return imin;
+}
+
+void Curve::clean_dupes() {
+
+ bool dirty = false;
+
+ for (int i = 1; i < _points.size(); ++i) {
+ real_t diff = _points[i - 1].pos.x - _points[i].pos.x;
+ if (diff <= CMP_EPSILON) {
+ _points.remove(i);
+ --i;
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ mark_dirty();
+}
+
+void Curve::set_point_left_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].left_tangent = tangent;
+ mark_dirty();
+}
+
+void Curve::set_point_right_tangent(int i, real_t tangent) {
+ ERR_FAIL_INDEX(i, _points.size());
+ _points[i].right_tangent = tangent;
+ mark_dirty();
+}
+
+real_t Curve::get_point_left_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].left_tangent;
+}
+
+real_t Curve::get_point_right_tangent(int i) const {
+ ERR_FAIL_INDEX_V(i, _points.size(), 0);
+ return _points[i].right_tangent;
+}
+
+void Curve::remove_point(int p_index) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points.remove(p_index);
+ mark_dirty();
+}
+
+void Curve::clear_points() {
+ _points.clear();
+ mark_dirty();
+}
+
+void Curve::set_point_value(int p_index, real_t pos) {
+ ERR_FAIL_INDEX(p_index, _points.size());
+ _points[p_index].pos.y = pos;
+ mark_dirty();
+}
+
+int Curve::set_point_offset(int p_index, float offset) {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
+ Point p = _points[p_index];
+ remove_point(p_index);
+ int i = add_point(Vector2(offset, p.pos.y));
+ _points[i].left_tangent = p.left_tangent;
+ _points[i].right_tangent = p.right_tangent;
+ return i;
+}
+
+Vector2 Curve::get_point_pos(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
+ return _points[p_index].pos;
+}
+
+real_t Curve::interpolate(real_t offset) const {
+ if (_points.size() == 0)
+ return 0;
+ if (_points.size() == 1)
+ return _points[0].pos.y;
+
+ int i = get_index(offset);
+
+ if (i == _points.size() - 1)
+ return _points[i].pos.y;
+
+ real_t local = offset - _points[i].pos.x;
+
+ if (i == 0 && local <= 0)
+ return _points[0].pos.y;
+
+ return interpolate_local_nocheck(i, local);
+}
+
+real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
+
+ const Point a = _points[index];
+ const Point b = _points[index + 1];
+
+ // Cubic bezier
+
+ // ac-----bc
+ // / \
+ // / \ Here with a.right_tangent > 0
+ // / \ and b.left_tangent < 0
+ // / \
+ // a b
+ //
+ // |-d1--|-d2--|-d3--|
+ //
+ // d1 == d2 == d3 == d / 3
+
+ // Control points are chosen at equal distances
+ real_t d = b.pos.x - a.pos.x;
+ if (Math::abs(d) <= CMP_EPSILON)
+ return b.pos.y;
+ local_offset /= d;
+ d /= 3.0;
+ real_t yac = a.pos.y + d * a.right_tangent;
+ real_t ybc = b.pos.y - d * b.left_tangent;
+
+ real_t y = _bezier_interp(local_offset, a.pos.y, yac, ybc, b.pos.y);
+
+ return y;
+}
+
+void Curve::mark_dirty() {
+ _baked_cache_dirty = true;
+ emit_signal(CoreStringNames::get_singleton()->changed);
+}
+
+Array Curve::get_data() const {
+
+ Array output;
+ output.resize(_points.size() * 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ const Point p = _points[j];
+ int i = j * 3;
+
+ output[i] = p.pos;
+ output[i + 1] = p.left_tangent;
+ output[i + 2] = p.right_tangent;
+ }
+
+ return output;
+}
+
+void Curve::set_data(Array input) {
+ ERR_FAIL_COND(input.size() % 3 != 0);
+
+#ifdef TOOLS_ENABLED
+ if (_disable_set_data)
+ return;
+#endif
+
+ _points.clear();
+
+ // Validate input
+ for (int i = 0; i < input.size(); i += 3) {
+ ERR_FAIL_COND(input[i].get_type() != Variant::VECTOR2);
+ ERR_FAIL_COND(input[i + 1].get_type() != Variant::REAL);
+ ERR_FAIL_COND(input[i + 2].get_type() != Variant::REAL);
+ }
+
+ _points.resize(input.size() / 3);
+
+ for (int j = 0; j < _points.size(); ++j) {
+
+ Point &p = _points[j];
+ int i = j * 3;
+
+ p.pos = input[i];
+ p.left_tangent = input[i + 1];
+ p.right_tangent = input[i + 2];
+ }
+
+ mark_dirty();
+}
+
+void Curve::bake() {
+ _baked_cache.clear();
+
+ _baked_cache.resize(_bake_resolution);
+
+ for (int i = 1; i < _bake_resolution - 1; ++i) {
+ real_t x = i / static_cast<real_t>(_bake_resolution);
+ real_t y = interpolate(x);
+ _baked_cache[i] = y;
+ }
+
+ if (_points.size() != 0) {
+ _baked_cache[0] = _points[0].pos.y;
+ _baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y;
+ }
+
+ _baked_cache_dirty = false;
+}
+
+void Curve::set_bake_resolution(int p_resolution) {
+ ERR_FAIL_COND(p_resolution < 1);
+ ERR_FAIL_COND(p_resolution > 1000);
+ _bake_resolution = p_resolution;
+ _baked_cache_dirty = true;
+}
+
+real_t Curve::interpolate_baked(real_t offset) {
+ if (_baked_cache_dirty) {
+ // Last-second bake if not done already
+ bake();
+ }
+
+ // Special cases if the cache is too small
+ if (_baked_cache.size() == 0) {
+ if (_points.size() == 0)
+ return 0;
+ return _points[0].pos.y;
+ } else if (_baked_cache.size() == 1) {
+ return _baked_cache[0];
+ }
+
+ // Get interpolation index
+ real_t fi = offset * _baked_cache.size();
+ int i = Math::floor(fi);
+ if (i < 0) {
+ i = 0;
+ fi = 0;
+ } else if (i >= _baked_cache.size()) {
+ i = _baked_cache.size() - 1;
+ fi = 0;
+ }
+
+ // Interpolate
+ if (i + 1 < _baked_cache.size()) {
+ real_t t = fi - i;
+ return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t);
+ } else {
+ return _baked_cache[_baked_cache.size() - 1];
+ }
+}
+
+void Curve::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_point", "pos", "left_tangent", "right_tangent"), &Curve::add_point, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point);
+ ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points);
+ ClassDB::bind_method(D_METHOD("get_point_pos", "index"), &Curve::get_point_pos);
+ ClassDB::bind_method(D_METHOD("set_point_value", "index, y"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("set_point_offset", "index, offset"), &Curve::set_point_value);
+ ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate);
+ ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked);
+ ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("set_point_left_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("set_point_right_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
+ ClassDB::bind_method(D_METHOD("clean_dupes"), &Curve::clean_dupes);
+ ClassDB::bind_method(D_METHOD("bake"), &Curve::bake);
+ ClassDB::bind_method(D_METHOD("get_bake_resolution"), &Curve::get_bake_resolution);
+ ClassDB::bind_method(D_METHOD("set_bake_resolution", "resolution"), &Curve::set_bake_resolution);
+ ClassDB::bind_method(D_METHOD("_get_data"), &Curve::get_data);
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve::set_data);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+}
+
int Curve2D::get_point_count() const {
return points.size();
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 17c0ac9f5e..63b9a07f07 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -82,6 +82,78 @@ public:
#endif
+// y(x) curve
+class Curve : public Resource {
+ GDCLASS(Curve, Resource)
+public:
+ static const int MIN_X = 0.f;
+ static const int MAX_X = 1.f;
+
+#ifdef TOOLS_ENABLED
+ bool _disable_set_data;
+#endif
+
+ struct Point {
+ Vector2 pos;
+ real_t left_tangent;
+ real_t right_tangent;
+
+ Point() {
+ left_tangent = 0;
+ right_tangent = 0;
+ }
+
+ Point(Vector2 p, real_t left = 0, real_t right = 0) {
+ pos = p;
+ left_tangent = left;
+ right_tangent = right;
+ }
+ };
+
+ Curve();
+
+ int get_point_count() const { return _points.size(); }
+
+ int add_point(Vector2 p_pos, real_t left_tangent = 0, real_t right_tangent = 0);
+ void remove_point(int p_index);
+ void clear_points();
+
+ int get_index(real_t offset) const;
+
+ void set_point_value(int p_index, real_t pos);
+ int set_point_offset(int p_index, float offset);
+ Vector2 get_point_pos(int p_index) const;
+
+ real_t interpolate(real_t offset) const;
+ real_t interpolate_local_nocheck(int index, real_t local_offset) const;
+
+ void clean_dupes();
+
+ void set_point_left_tangent(int i, real_t tangent);
+ void set_point_right_tangent(int i, real_t tangent);
+ real_t get_point_left_tangent(int i) const;
+ real_t get_point_right_tangent(int i) const;
+
+ Array get_data() const;
+ void set_data(Array input);
+
+ void bake();
+ int get_bake_resolution() const { return _bake_resolution; }
+ void set_bake_resolution(int p_interval);
+ real_t interpolate_baked(real_t offset);
+
+protected:
+ static void _bind_methods();
+
+private:
+ void mark_dirty();
+
+ Vector<Point> _points;
+ bool _baked_cache_dirty;
+ Vector<real_t> _baked_cache;
+ int _bake_resolution;
+};
+
class Curve2D : public Resource {
GDCLASS(Curve2D, Resource);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index a435ba06cc..24e3977de8 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -959,7 +959,6 @@ void Environment::_bind_methods() {
ADD_GROUP("SS Reflections", "ss_reflections_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ss_reflections_enabled"), "set_ssr_enabled", "is_ssr_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ss_reflections_max_steps", PROPERTY_HINT_RANGE, "1,512,1"), "set_ssr_max_steps", "get_ssr_max_steps");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_accel", PROPERTY_HINT_RANGE, "0,4,0.01"), "set_ssr_accel", "get_ssr_accel");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_fade_in", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_in", "get_ssr_fade_in");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_fade_out", PROPERTY_HINT_EXP_EASING), "set_ssr_fade_out", "get_ssr_fade_out");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ss_reflections_depth_tolerance", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_ssr_depth_tolerance", "get_ssr_depth_tolerance");
@@ -1040,7 +1039,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_far_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_far_amount", "get_dof_blur_far_amount");
ADD_PROPERTY(PropertyInfo(Variant::INT, "dof_blur_far_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_dof_blur_far_quality", "get_dof_blur_far_quality");
- ADD_GROUP("DOF Far Near", "dof_blur_near_");
+ ADD_GROUP("DOF Near Blur", "dof_blur_near_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_near_distance", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_distance", "get_dof_blur_near_distance");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "dof_blur_near_transition", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01"), "set_dof_blur_near_transition", "get_dof_blur_near_transition");
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index a3180ee1df..ef7011b2af 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -668,7 +668,11 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "surfaces/" + itos(i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
- p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "Material", PROPERTY_USAGE_EDITOR));
+ if (surfaces[i].is_2d) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,CanvasItemMaterial", PROPERTY_USAGE_EDITOR));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial", PROPERTY_USAGE_EDITOR));
+ }
}
p_list->push_back(PropertyInfo(Variant::RECT3, "custom_aabb/custom_aabb"));
@@ -692,6 +696,7 @@ void ArrayMesh::add_surface(uint32_t p_format, PrimitiveType p_primitive, const
Surface s;
s.aabb = p_aabb;
+ s.is_2d = p_format & ARRAY_FLAG_USE_2D_VERTICES;
surfaces.push_back(s);
_recompute_aabb();
@@ -709,7 +714,8 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
/* make aABB? */ {
- PoolVector<Vector3> vertices = p_arrays[ARRAY_VERTEX];
+ Variant arr = p_arrays[ARRAY_VERTEX];
+ PoolVector<Vector3> vertices = arr;
int len = vertices.size();
ERR_FAIL_COND(len == 0);
PoolVector<Vector3>::Read r = vertices.read();
@@ -726,6 +732,7 @@ void ArrayMesh::add_surface_from_arrays(PrimitiveType p_primitive, const Array &
}
surfaces[surfaces.size() - 1].aabb = aabb;
+ surfaces[surfaces.size() - 1].is_2d = arr.get_type() == Variant::POOL_VECTOR2_ARRAY;
_recompute_aabb();
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 37fddf3aa6..f716b59fe9 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -147,6 +147,7 @@ private:
String name;
Rect3 aabb;
Ref<Material> material;
+ bool is_2d;
};
Vector<Surface> surfaces;
RID mesh;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 2120b37497..7b393233f1 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -30,7 +30,9 @@
#include "texture.h"
#include "core/method_bind_ext.inc"
#include "core/os/os.h"
+#include "core_string_names.h"
#include "io/image_loader.h"
+
Size2 Texture::get_size() const {
return Size2(get_width(), get_height());
@@ -1376,254 +1378,126 @@ void CurveTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
- ClassDB::bind_method(D_METHOD("set_points", "points"), &CurveTexture::set_points);
- ClassDB::bind_method(D_METHOD("get_points"), &CurveTexture::get_points);
+ ClassDB::bind_method(D_METHOD("set_curve", "curve:Curve"), &CurveTexture::set_curve);
+ ClassDB::bind_method(D_METHOD("get_curve:Curve"), &CurveTexture::get_curve);
+
+ ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min", PROPERTY_HINT_RANGE, "-1024,1024"), "set_min", "get_min");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max", PROPERTY_HINT_RANGE, "-1024,1024"), "set_max", "get_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
}
void CurveTexture::set_max(float p_max) {
- max = p_max;
+ _max = p_max;
emit_changed();
}
float CurveTexture::get_max() const {
- return max;
+ return _max;
}
void CurveTexture::set_min(float p_min) {
- min = p_min;
+ _min = p_min;
emit_changed();
}
float CurveTexture::get_min() const {
- return min;
+ return _min;
}
void CurveTexture::set_width(int p_width) {
ERR_FAIL_COND(p_width < 32 || p_width > 4096);
- width = p_width;
- if (points.size())
- set_points(points);
+ _width = p_width;
+ _update();
}
int CurveTexture::get_width() const {
- return width;
+ return _width;
}
-static void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d, float *p_heights, bool *p_useds, int p_width, float p_min, float p_max) {
-
- float geometry[4][4];
- float tmp1[4][4];
- float tmp2[4][4];
- float deltas[4][4];
- double x, dx, dx2, dx3;
- double y, dy, dy2, dy3;
- double d, d2, d3;
- int lastx;
- int newx;
- float lasty;
- float newy;
- int ntimes;
- int i, j;
-
- int xmax = p_width;
-
- /* construct the geometry matrix from the segment */
- for (i = 0; i < 4; i++) {
- geometry[i][2] = 0;
- geometry[i][3] = 0;
- }
+void CurveTexture::ensure_default_setup() {
- geometry[0][0] = (p_a[0] * xmax);
- geometry[1][0] = (p_b[0] * xmax);
- geometry[2][0] = (p_c[0] * xmax);
- geometry[3][0] = (p_d[0] * xmax);
-
- geometry[0][1] = (p_a[1]);
- geometry[1][1] = (p_b[1]);
- geometry[2][1] = (p_c[1]);
- geometry[3][1] = (p_d[1]);
-
- /* subdivide the curve ntimes (1000) times */
- ntimes = 4 * xmax;
- /* ntimes can be adjusted to give a finer or coarser curve */
- d = 1.0 / ntimes;
- d2 = d * d;
- d3 = d * d * d;
-
- /* construct a temporary matrix for determining the forward differencing deltas */
- tmp2[0][0] = 0;
- tmp2[0][1] = 0;
- tmp2[0][2] = 0;
- tmp2[0][3] = 1;
- tmp2[1][0] = d3;
- tmp2[1][1] = d2;
- tmp2[1][2] = d;
- tmp2[1][3] = 0;
- tmp2[2][0] = 6 * d3;
- tmp2[2][1] = 2 * d2;
- tmp2[2][2] = 0;
- tmp2[2][3] = 0;
- tmp2[3][0] = 6 * d3;
- tmp2[3][1] = 0;
- tmp2[3][2] = 0;
- tmp2[3][3] = 0;
-
- /* compose the basis and geometry matrices */
-
- static const float CR_basis[4][4] = {
- { -0.5, 1.5, -1.5, 0.5 },
- { 1.0, -2.5, 2.0, -0.5 },
- { -0.5, 0.0, 0.5, 0.0 },
- { 0.0, 1.0, 0.0, 0.0 },
- };
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
- CR_basis[i][1] * geometry[1][j] +
- CR_basis[i][2] * geometry[2][j] +
- CR_basis[i][3] * geometry[3][j]);
- }
+ if (_curve.is_null()) {
+ Ref<Curve> curve = Ref<Curve>(memnew(Curve));
+ curve->add_point(Vector2(0, 1));
+ curve->add_point(Vector2(1, 1));
+ set_curve(curve);
}
- /* compose the above results to get the deltas matrix */
-
- for (i = 0; i < 4; i++) {
- for (j = 0; j < 4; j++) {
- deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
- tmp2[i][1] * tmp1[1][j] +
- tmp2[i][2] * tmp1[2][j] +
- tmp2[i][3] * tmp1[3][j]);
- }
+
+ if (get_min() == 0 && get_max() == 1) {
+ set_max(32);
}
+}
- /* extract the x deltas */
- x = deltas[0][0];
- dx = deltas[1][0];
- dx2 = deltas[2][0];
- dx3 = deltas[3][0];
-
- /* extract the y deltas */
- y = deltas[0][1];
- dy = deltas[1][1];
- dy2 = deltas[2][1];
- dy3 = deltas[3][1];
-
- lastx = CLAMP(x, 0, xmax);
- lasty = y;
-
- p_heights[lastx] = lasty;
- p_useds[lastx] = true;
-
- /* loop over the curve */
- for (i = 0; i < ntimes; i++) {
- /* increment the x values */
- x += dx;
- dx += dx2;
- dx2 += dx3;
-
- /* increment the y values */
- y += dy;
- dy += dy2;
- dy2 += dy3;
-
- newx = CLAMP((Math::round(x)), 0, xmax);
- newy = CLAMP(y, p_min, p_max);
-
- /* if this point is different than the last one...then draw it */
- if ((lastx != newx) || (lasty != newy)) {
- p_useds[newx] = true;
- p_heights[newx] = newy;
+void CurveTexture::set_curve(Ref<Curve> p_curve) {
+ if (_curve != p_curve) {
+ if (_curve.is_valid()) {
+ _curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_update");
}
-
- lastx = newx;
- lasty = newy;
+ _curve = p_curve;
+ if (_curve.is_valid()) {
+ _curve->connect(CoreStringNames::get_singleton()->changed, this, "_update");
+ }
+ _update();
}
}
-void CurveTexture::set_points(const PoolVector<Vector2> &p_points) {
-
- points = p_points;
+void CurveTexture::_update() {
PoolVector<uint8_t> data;
- PoolVector<bool> used;
- data.resize(width * sizeof(float));
- used.resize(width);
+ data.resize(_width * sizeof(float));
+
+ // The array is locked in that scope
{
PoolVector<uint8_t>::Write wd8 = data.write();
float *wd = (float *)wd8.ptr();
- PoolVector<bool>::Write wu = used.write();
- int pc = p_points.size();
- PoolVector<Vector2>::Read pr = p_points.read();
- for (int i = 0; i < width; i++) {
- wd[i] = 0.0;
- wu[i] = false;
- }
-
- Vector2 prev = Vector2(0, 0);
- Vector2 prev2 = Vector2(0, 0);
-
- for (int i = -1; i < pc; i++) {
-
- Vector2 next;
- Vector2 next2;
- if (i + 1 >= pc) {
- next = Vector2(1, 0);
- } else {
- next = Vector2(pr[i + 1].x, pr[i + 1].y);
+ if (_curve.is_valid()) {
+ Curve &curve = **_curve;
+ float height = _max - _min;
+ for (int i = 0; i < _width; ++i) {
+ float t = i / static_cast<float>(_width);
+ float v = curve.interpolate_baked(t);
+ wd[i] = CLAMP(_min + v * height, _min, _max);
}
- if (i + 2 >= pc) {
- next2 = Vector2(1, 0);
- } else {
- next2 = Vector2(pr[i + 2].x, pr[i + 2].y);
+ } else {
+ for (int i = 0; i < _width; ++i) {
+ wd[i] = 0;
}
-
- /*if (i==-1 && prev.offset==next.offset) {
- prev=next;
- continue;
- }*/
-
- _plot_curve(prev2, prev, next, next2, wd, wu.ptr(), width, min, max);
-
- prev2 = prev;
- prev = next;
}
}
- Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RF, data));
+ Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data));
- VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
- VS::get_singleton()->texture_set_data(texture, image);
+ VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
+ VS::get_singleton()->texture_set_data(_texture, image);
emit_changed();
}
-PoolVector<Vector2> CurveTexture::get_points() const {
+Ref<Curve> CurveTexture::get_curve() const {
- return points;
+ return _curve;
}
RID CurveTexture::get_rid() const {
- return texture;
+ return _texture;
}
CurveTexture::CurveTexture() {
- max = 1;
- min = 0;
- width = 2048;
- texture = VS::get_singleton()->texture_create();
+ _max = 1;
+ _min = 0;
+ _width = 2048;
+ _texture = VS::get_singleton()->texture_create();
}
CurveTexture::~CurveTexture() {
- VS::get_singleton()->free(texture);
+ VS::get_singleton()->free(_texture);
}
//////////////////
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 2b82dbd21f..2c36cdef87 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -30,6 +30,7 @@
#ifndef TEXTURE_H
#define TEXTURE_H
+#include "curve.h"
#include "io/resource_loader.h"
#include "math_2d.h"
#include "resource.h"
@@ -389,20 +390,22 @@ public:
~CubeMap();
};
-VARIANT_ENUM_CAST(CubeMap::Flags);
-VARIANT_ENUM_CAST(CubeMap::Side);
-VARIANT_ENUM_CAST(CubeMap::Storage);
+VARIANT_ENUM_CAST(CubeMap::Flags)
+VARIANT_ENUM_CAST(CubeMap::Side)
+VARIANT_ENUM_CAST(CubeMap::Storage)
class CurveTexture : public Texture {
- GDCLASS(CurveTexture, Texture);
- RES_BASE_EXTENSION("curvetex");
+ GDCLASS(CurveTexture, Texture)
+ RES_BASE_EXTENSION("curvetex")
private:
- RID texture;
- PoolVector<Vector2> points;
- float min, max;
- int width;
+ RID _texture;
+ Ref<Curve> _curve;
+ float _min, _max;
+ int _width;
+
+ void _update();
protected:
static void _bind_methods();
@@ -417,8 +420,10 @@ public:
void set_width(int p_width);
int get_width() const;
- void set_points(const PoolVector<Vector2> &p_points);
- PoolVector<Vector2> get_points() const;
+ void ensure_default_setup();
+
+ void set_curve(Ref<Curve> p_curve);
+ Ref<Curve> get_curve() const;
virtual RID get_rid() const;
diff --git a/servers/physics_2d/area_pair_2d_sw.cpp b/servers/physics_2d/area_pair_2d_sw.cpp
index c98375fc44..184db944da 100644
--- a/servers/physics_2d/area_pair_2d_sw.cpp
+++ b/servers/physics_2d/area_pair_2d_sw.cpp
@@ -32,7 +32,13 @@
bool AreaPair2DSW::setup(real_t p_step) {
- bool result = area->test_collision_mask(body) && CollisionSolver2DSW::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), NULL, this);
+ bool result = false;
+
+ if (area->is_shape_set_as_disabled(area_shape) || body->is_shape_set_as_disabled(body_shape)) {
+ result = false;
+ } else if (area->test_collision_mask(body) && CollisionSolver2DSW::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), NULL, this)) {
+ result = true;
+ }
if (result != colliding) {
@@ -90,7 +96,12 @@ AreaPair2DSW::~AreaPair2DSW() {
bool Area2Pair2DSW::setup(real_t p_step) {
- bool result = area_a->test_collision_mask(area_b) && CollisionSolver2DSW::solve(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), Vector2(), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), Vector2(), NULL, this);
+ bool result = false;
+ if (area_a->is_shape_set_as_disabled(shape_a) || area_b->is_shape_set_as_disabled(shape_b)) {
+ result = false;
+ } else if (area_a->test_collision_mask(area_b) && CollisionSolver2DSW::solve(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), Vector2(), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), Vector2(), NULL, this)) {
+ result = true;
+ }
if (result != colliding) {
diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp
index 03ad66d4e9..538ea10211 100644
--- a/servers/physics_2d/body_2d_sw.cpp
+++ b/servers/physics_2d/body_2d_sw.cpp
@@ -676,8 +676,6 @@ Body2DSW::Body2DSW()
area_linear_damp = 0;
contact_count = 0;
gravity_scale = 1.0;
- using_one_way_cache = false;
- one_way_collision_max_depth = 0.1;
first_integration = false;
still_time = 0;
diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h
index 23adebbad6..9e5deef3f2 100644
--- a/servers/physics_2d/body_2d_sw.h
+++ b/servers/physics_2d/body_2d_sw.h
@@ -67,9 +67,6 @@ class Body2DSW : public CollisionObject2DSW {
Vector2 applied_force;
real_t applied_torque;
- Vector2 one_way_collision_direction;
- real_t one_way_collision_max_depth;
-
SelfList<Body2DSW> active_list;
SelfList<Body2DSW> inertia_update_list;
SelfList<Body2DSW> direct_state_query_list;
@@ -81,7 +78,6 @@ class Body2DSW : public CollisionObject2DSW {
bool can_sleep;
bool first_time_kinematic;
bool first_integration;
- bool using_one_way_cache;
void _update_inertia();
virtual void _shapes_changed();
Transform2D new_transform;
@@ -246,17 +242,6 @@ public:
_FORCE_INLINE_ void set_continuous_collision_detection_mode(Physics2DServer::CCDMode p_mode) { continuous_cd_mode = p_mode; }
_FORCE_INLINE_ Physics2DServer::CCDMode get_continuous_collision_detection_mode() const { return continuous_cd_mode; }
- void set_one_way_collision_direction(const Vector2 &p_dir) {
- one_way_collision_direction = p_dir;
- using_one_way_cache = one_way_collision_direction != Vector2();
- }
- Vector2 get_one_way_collision_direction() const { return one_way_collision_direction; }
-
- void set_one_way_collision_max_depth(real_t p_depth) { one_way_collision_max_depth = p_depth; }
- real_t get_one_way_collision_max_depth() const { return one_way_collision_max_depth; }
-
- _FORCE_INLINE_ bool is_using_one_way_collision() const { return using_one_way_cache; }
-
void set_space(Space2DSW *p_space);
void update_inertias();
diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp
index 47e9afbde6..484d4503d0 100644
--- a/servers/physics_2d/body_pair_2d_sw.cpp
+++ b/servers/physics_2d/body_pair_2d_sw.cpp
@@ -225,6 +225,11 @@ bool BodyPair2DSW::setup(real_t p_step) {
return false;
}
+ if (A->is_shape_set_as_disabled(shape_A) || B->is_shape_set_as_disabled(shape_B)) {
+ collided = false;
+ return false;
+ }
+
//use local A coordinates to avoid numerical issues on collision detection
offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
@@ -280,8 +285,8 @@ bool BodyPair2DSW::setup(real_t p_step) {
//if (!prev_collided) {
{
- if (A->is_using_one_way_collision()) {
- Vector2 direction = A->get_one_way_collision_direction();
+ if (A->is_shape_set_as_one_way_collision(shape_A)) {
+ Vector2 direction = xform_A.get_axis(1).normalized();
bool valid = false;
if (B->get_linear_velocity().dot(direction) >= 0) {
for (int i = 0; i < contact_count; i++) {
@@ -303,8 +308,8 @@ bool BodyPair2DSW::setup(real_t p_step) {
}
}
- if (B->is_using_one_way_collision()) {
- Vector2 direction = B->get_one_way_collision_direction();
+ if (B->is_shape_set_as_one_way_collision(shape_B)) {
+ Vector2 direction = xform_B.get_axis(1).normalized();
bool valid = false;
if (A->get_linear_velocity().dot(direction) >= 0) {
for (int i = 0; i < contact_count; i++) {
@@ -390,7 +395,7 @@ bool BodyPair2DSW::setup(real_t p_step) {
}
}
- if (A->is_shape_set_as_trigger(shape_A) || B->is_shape_set_as_trigger(shape_B) || (A->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC && B->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC)) {
+ if ((A->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC && B->get_mode() <= Physics2DServer::BODY_MODE_KINEMATIC)) {
c.active = false;
collided = false;
continue;
diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp
index 0163a18850..8f13f1130a 100644
--- a/servers/physics_2d/collision_object_2d_sw.cpp
+++ b/servers/physics_2d/collision_object_2d_sw.cpp
@@ -37,7 +37,8 @@ void CollisionObject2DSW::add_shape(Shape2DSW *p_shape, const Transform2D &p_tra
s.xform = p_transform;
s.xform_inv = s.xform.affine_inverse();
s.bpid = 0; //needs update
- s.trigger = false;
+ s.disabled = false;
+ s.one_way_collision = false;
shapes.push_back(s);
p_shape->add_owner(this);
_update_shapes();
diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h
index f2059e8618..5e29132e8d 100644
--- a/servers/physics_2d/collision_object_2d_sw.h
+++ b/servers/physics_2d/collision_object_2d_sw.h
@@ -58,8 +58,12 @@ private:
Rect2 aabb_cache; //for rayqueries
Shape2DSW *shape;
Variant metadata;
- bool trigger;
- Shape() { trigger = false; }
+ bool disabled;
+ bool one_way_collision;
+ Shape() {
+ disabled = false;
+ one_way_collision = false;
+ }
};
Vector<Shape> shapes;
@@ -116,8 +120,11 @@ public:
_FORCE_INLINE_ Transform2D get_inv_transform() const { return inv_transform; }
_FORCE_INLINE_ Space2DSW *get_space() const { return space; }
- _FORCE_INLINE_ void set_shape_as_trigger(int p_idx, bool p_enable) { shapes[p_idx].trigger = p_enable; }
- _FORCE_INLINE_ bool is_shape_set_as_trigger(int p_idx) const { return shapes[p_idx].trigger; }
+ _FORCE_INLINE_ void set_shape_as_disabled(int p_idx, bool p_disabled) { shapes[p_idx].disabled = p_disabled; }
+ _FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const { return shapes[p_idx].disabled; }
+
+ _FORCE_INLINE_ void set_shape_as_one_way_collision(int p_idx, bool p_one_way_collision) { shapes[p_idx].one_way_collision = p_one_way_collision; }
+ _FORCE_INLINE_ bool is_shape_set_as_one_way_collision(int p_idx) const { return shapes[p_idx].one_way_collision; }
void set_collision_mask(uint32_t p_mask) { collision_mask = p_mask; }
_FORCE_INLINE_ uint32_t get_collision_mask() const { return collision_mask; }
diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp
index 9a31fa49b0..1d88710f1a 100644
--- a/servers/physics_2d/physics_2d_server_sw.cpp
+++ b/servers/physics_2d/physics_2d_server_sw.cpp
@@ -352,6 +352,15 @@ void Physics2DServerSW::area_set_shape_transform(RID p_area, int p_shape_idx, co
area->set_shape_transform(p_shape_idx, p_transform);
}
+void Physics2DServerSW::area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled) {
+
+ Area2DSW *area = area_owner.get(p_area);
+ ERR_FAIL_COND(!area);
+
+ ERR_FAIL_INDEX(p_shape, area->get_shape_count());
+ area->set_shape_as_disabled(p_shape, p_disabled);
+}
+
int Physics2DServerSW::area_get_shape_count(RID p_area) const {
Area2DSW *area = area_owner.get(p_area);
@@ -640,24 +649,23 @@ void Physics2DServerSW::body_clear_shapes(RID p_body) {
body->remove_shape(0);
}
-void Physics2DServerSW::body_set_shape_as_trigger(RID p_body, int p_shape_idx, bool p_enable) {
+void Physics2DServerSW::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
Body2DSW *body = body_owner.get(p_body);
ERR_FAIL_COND(!body);
ERR_FAIL_INDEX(p_shape_idx, body->get_shape_count());
- body->set_shape_as_trigger(p_shape_idx, p_enable);
+ body->set_shape_as_disabled(p_shape_idx, p_disabled);
}
+void Physics2DServerSW::body_set_shape_as_one_way_collision(RID p_body, int p_shape_idx, bool p_enable) {
-bool Physics2DServerSW::body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const {
-
- const Body2DSW *body = body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, false);
+ Body2DSW *body = body_owner.get(p_body);
+ ERR_FAIL_COND(!body);
- ERR_FAIL_INDEX_V(p_shape_idx, body->get_shape_count(), false);
+ ERR_FAIL_INDEX(p_shape_idx, body->get_shape_count());
- return body->is_shape_set_as_trigger(p_shape_idx);
+ body->set_shape_as_one_way_collision(p_shape_idx, p_enable);
}
void Physics2DServerSW::body_set_continuous_collision_detection_mode(RID p_body, CCDMode p_mode) {
@@ -887,34 +895,6 @@ int Physics2DServerSW::body_get_max_contacts_reported(RID p_body) const {
return body->get_max_contacts_reported();
}
-void Physics2DServerSW::body_set_one_way_collision_direction(RID p_body, const Vector2 &p_direction) {
-
- Body2DSW *body = body_owner.get(p_body);
- ERR_FAIL_COND(!body);
- body->set_one_way_collision_direction(p_direction);
-}
-
-Vector2 Physics2DServerSW::body_get_one_way_collision_direction(RID p_body) const {
-
- Body2DSW *body = body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, Vector2());
- return body->get_one_way_collision_direction();
-}
-
-void Physics2DServerSW::body_set_one_way_collision_max_depth(RID p_body, real_t p_max_depth) {
-
- Body2DSW *body = body_owner.get(p_body);
- ERR_FAIL_COND(!body);
- body->set_one_way_collision_max_depth(p_max_depth);
-}
-
-real_t Physics2DServerSW::body_get_one_way_collision_max_depth(RID p_body) const {
-
- Body2DSW *body = body_owner.get(p_body);
- ERR_FAIL_COND_V(!body, 0);
- return body->get_one_way_collision_max_depth();
-}
-
void Physics2DServerSW::body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata) {
Body2DSW *body = body_owner.get(p_body);
diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h
index be67e3157d..9cbdfc598f 100644
--- a/servers/physics_2d/physics_2d_server_sw.h
+++ b/servers/physics_2d/physics_2d_server_sw.h
@@ -123,6 +123,8 @@ public:
virtual RID area_get_shape(RID p_area, int p_shape_idx) const;
virtual Transform2D area_get_shape_transform(RID p_area, int p_shape_idx) const;
+ virtual void area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled);
+
virtual void area_remove_shape(RID p_area, int p_shape_idx);
virtual void area_clear_shapes(RID p_area);
@@ -167,8 +169,8 @@ public:
virtual void body_remove_shape(RID p_body, int p_shape_idx);
virtual void body_clear_shapes(RID p_body);
- virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx, bool p_enable);
- virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const;
+ virtual void body_set_shape_disabled(RID p_body, int p_shape, bool p_disabled);
+ virtual void body_set_shape_as_one_way_collision(RID p_body, int p_shape, bool p_enabled);
virtual void body_attach_object_instance_ID(RID p_body, uint32_t p_ID);
virtual uint32_t body_get_object_instance_ID(RID p_body) const;
@@ -212,12 +214,6 @@ public:
virtual void body_set_max_contacts_reported(RID p_body, int p_contacts);
virtual int body_get_max_contacts_reported(RID p_body) const;
- virtual void body_set_one_way_collision_direction(RID p_body, const Vector2 &p_direction);
- virtual Vector2 body_get_one_way_collision_direction(RID p_body) const;
-
- virtual void body_set_one_way_collision_max_depth(RID p_body, real_t p_max_depth);
- virtual real_t body_get_one_way_collision_max_depth(RID p_body) const;
-
virtual void body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata = Variant());
virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count);
diff --git a/servers/physics_2d/physics_2d_server_wrap_mt.h b/servers/physics_2d/physics_2d_server_wrap_mt.h
index 1026d84fd9..9fcfebef6b 100644
--- a/servers/physics_2d/physics_2d_server_wrap_mt.h
+++ b/servers/physics_2d/physics_2d_server_wrap_mt.h
@@ -145,6 +145,7 @@ public:
FUNC3(area_add_shape, RID, RID, const Transform2D &);
FUNC3(area_set_shape, RID, int, RID);
FUNC3(area_set_shape_transform, RID, int, const Transform2D &);
+ FUNC3(area_set_shape_disabled, RID, int, bool);
FUNC1RC(int, area_get_shape_count, RID);
FUNC2RC(RID, area_get_shape, RID, int);
@@ -191,8 +192,8 @@ public:
FUNC2RC(Variant, body_get_shape_metadata, RID, int);
FUNC2RC(RID, body_get_shape, RID, int);
- FUNC3(body_set_shape_as_trigger, RID, int, bool);
- FUNC2RC(bool, body_is_shape_set_as_trigger, RID, int);
+ FUNC3(body_set_shape_disabled, RID, int, bool);
+ FUNC3(body_set_shape_as_one_way_collision, RID, int, bool);
FUNC2(body_remove_shape, RID, int);
FUNC1(body_clear_shapes, RID);
@@ -232,12 +233,6 @@ public:
FUNC2(body_set_max_contacts_reported, RID, int);
FUNC1RC(int, body_get_max_contacts_reported, RID);
- FUNC2(body_set_one_way_collision_direction, RID, const Vector2 &);
- FUNC1RC(Vector2, body_get_one_way_collision_direction, RID);
-
- FUNC2(body_set_one_way_collision_max_depth, RID, real_t);
- FUNC1RC(real_t, body_get_one_way_collision_max_depth, RID);
-
FUNC2(body_set_contacts_reported_depth_treshold, RID, real_t);
FUNC1RC(real_t, body_get_contacts_reported_depth_treshold, RID);
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index fd94fc01cd..0b31ff144b 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -265,14 +265,6 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transfor
//test initial overlap
if (CollisionSolver2DSW::solve(shape, p_xform, Vector2(), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), NULL, NULL, NULL, p_margin)) {
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
- //if one way collision direction ignore initial overlap
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- if (body->get_one_way_collision_direction() != Vector2()) {
- continue;
- }
- }
-
return false;
}
@@ -297,27 +289,6 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transfor
}
}
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
-
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- if (body->get_one_way_collision_direction() != Vector2()) {
-
- Vector2 cd[2];
- Physics2DServerSW::CollCbkData cbk;
- cbk.max = 1;
- cbk.amount = 0;
- cbk.ptr = cd;
- cbk.valid_dir = body->get_one_way_collision_direction();
- cbk.valid_depth = body->get_one_way_collision_max_depth();
-
- Vector2 sep = mnormal; //important optimization for this to work fast enough
- bool collided = CollisionSolver2DSW::solve(shape, p_xform, p_motion * (hi + space->contact_max_allowed_penetration), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), Physics2DServerSW::_shape_col_cbk, &cbk, &sep, p_margin);
- if (!collided || cbk.amount == 0) {
- continue;
- }
- }
- }
-
if (low < best_safe) {
best_safe = low;
best_unsafe = hi;
@@ -369,15 +340,9 @@ bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Transform2D &
if (p_exclude.has(col_obj->get_self()))
continue;
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- cbk.valid_dir = body->get_one_way_collision_direction();
- cbk.valid_depth = body->get_one_way_collision_max_depth();
- } else {
- cbk.valid_dir = Vector2();
- cbk.valid_depth = 0;
- }
+ cbk.valid_dir = Vector2();
+ cbk.valid_depth = 0;
if (CollisionSolver2DSW::solve(shape, p_shape_xform, p_motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, NULL, p_margin)) {
collided = p_result_max == 0 || cbk.amount > 0;
@@ -407,13 +372,10 @@ static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B,
_RestCallbackData2D *rd = (_RestCallbackData2D *)p_userdata;
if (rd->valid_dir != Vector2()) {
-
- if (rd->valid_dir != Vector2()) {
- if (p_point_A.distance_squared_to(p_point_B) > rd->valid_depth * rd->valid_depth)
- return;
- if (rd->valid_dir.dot((p_point_A - p_point_B).normalized()) < Math_PI * 0.25)
- return;
- }
+ if (p_point_A.distance_squared_to(p_point_B) > rd->valid_depth * rd->valid_depth)
+ return;
+ if (rd->valid_dir.dot((p_point_A - p_point_B).normalized()) < Math_PI * 0.25)
+ return;
}
Vector2 contact_rel = p_point_B - p_point_A;
@@ -455,16 +417,8 @@ bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_sh
if (p_exclude.has(col_obj->get_self()))
continue;
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
-
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- rcd.valid_dir = body->get_one_way_collision_direction();
- rcd.valid_depth = body->get_one_way_collision_max_depth();
- } else {
- rcd.valid_dir = Vector2();
- rcd.valid_depth = 0;
- }
-
+ rcd.valid_dir = Vector2();
+ rcd.valid_depth = 0;
rcd.object = col_obj;
rcd.shape = shape_idx;
bool sc = CollisionSolver2DSW::solve(shape, p_shape_xform, p_motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), _rest_cbk_result, &rcd, NULL, p_margin);
@@ -517,7 +471,7 @@ int Space2DSW::_cull_aabb_for_body(Body2DSW *p_body, const Rect2 &p_aabb) {
keep = false;
else if (static_cast<Body2DSW *>(intersection_query_results[i])->has_exception(p_body->get_self()) || p_body->has_exception(intersection_query_results[i]->get_self()))
keep = false;
- else if (static_cast<Body2DSW *>(intersection_query_results[i])->is_shape_set_as_trigger(intersection_query_subindex_results[i]))
+ else if (static_cast<Body2DSW *>(intersection_query_results[i])->is_shape_set_as_disabled(intersection_query_subindex_results[i]))
keep = false;
if (!keep) {
@@ -589,7 +543,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
int amount = _cull_aabb_for_body(p_body, body_aabb);
for (int j = 0; j < p_body->get_shape_count(); j++) {
- if (p_body->is_shape_set_as_trigger(j))
+ if (p_body->is_shape_set_as_disabled(j))
continue;
Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
@@ -599,18 +553,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
const CollisionObject2DSW *col_obj = intersection_query_results[i];
int shape_idx = intersection_query_subindex_results[i];
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
-
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
-
- Vector2 cdir = body->get_one_way_collision_direction();
- /*
- if (cdir!=Vector2() && p_motion.dot(cdir)<0)
- continue;
- */
+ if (col_obj->is_shape_set_as_one_way_collision(j)) {
- cbk.valid_dir = cdir;
- cbk.valid_depth = body->get_one_way_collision_max_depth();
+ cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ cbk.valid_depth = p_margin; //only valid depth is the collision margin
} else {
cbk.valid_dir = Vector2();
cbk.valid_depth = 0;
@@ -678,7 +624,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
for (int j = 0; j < p_body->get_shape_count(); j++) {
- if (p_body->is_shape_set_as_trigger(j))
+ if (p_body->is_shape_set_as_disabled(j))
continue;
Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);
@@ -703,12 +649,8 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
//test initial overlap
if (CollisionSolver2DSW::solve(body_shape, body_shape_xform, Vector2(), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), NULL, NULL, NULL, 0)) {
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
- //if one way collision direction ignore initial overlap
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- if (body->get_one_way_collision_direction() != Vector2()) {
- continue;
- }
+ if (col_obj->is_shape_set_as_one_way_collision(j)) {
+ continue;
}
stuck = true;
@@ -720,7 +662,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
real_t hi = 1;
Vector2 mnormal = p_motion.normalized();
- for (int i = 0; i < 8; i++) { //steps should be customizable..
+ for (int k = 0; k < 8; k++) { //steps should be customizable..
real_t ofs = (low + hi) * 0.5;
@@ -739,15 +681,16 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- if (body->get_one_way_collision_direction() != Vector2()) {
+ if (col_obj->is_shape_set_as_one_way_collision(j)) {
Vector2 cd[2];
Physics2DServerSW::CollCbkData cbk;
cbk.max = 1;
cbk.amount = 0;
cbk.ptr = cd;
- cbk.valid_dir = body->get_one_way_collision_direction();
- cbk.valid_depth = body->get_one_way_collision_max_depth();
+ cbk.valid_dir = body_shape_xform.get_axis(1).normalized();
+ ;
+ cbk.valid_depth = 10e20;
Vector2 sep = mnormal; //important optimization for this to work fast enough
bool collided = CollisionSolver2DSW::solve(body_shape, body_shape_xform, p_motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), Physics2DServerSW::_shape_col_cbk, &cbk, &sep, 0);
@@ -816,11 +759,10 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
const CollisionObject2DSW *col_obj = intersection_query_results[i];
int shape_idx = intersection_query_subindex_results[i];
- if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) {
+ if (col_obj->is_shape_set_as_one_way_collision(shape_idx)) {
- const Body2DSW *body = static_cast<const Body2DSW *>(col_obj);
- rcd.valid_dir = body->get_one_way_collision_direction();
- rcd.valid_depth = body->get_one_way_collision_max_depth();
+ rcd.valid_dir = body_shape_xform.get_axis(1).normalized();
+ rcd.valid_depth = 10e20;
} else {
rcd.valid_dir = Vector2();
rcd.valid_depth = 0;
@@ -839,6 +781,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co
r_result->collider = rcd.best_object->get_self();
r_result->collider_id = rcd.best_object->get_instance_id();
r_result->collider_shape = rcd.best_shape;
+ r_result->collision_local_shape = best_shape;
r_result->collision_normal = rcd.best_normal;
r_result->collision_point = rcd.best_contact;
r_result->collider_metadata = rcd.best_object->get_shape_metadata(rcd.best_shape);
diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp
index 8ffa82214c..55ea2b41e7 100644
--- a/servers/physics_2d_server.cpp
+++ b/servers/physics_2d_server.cpp
@@ -496,6 +496,7 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("area_add_shape", "area", "shape", "transform"), &Physics2DServer::area_add_shape, DEFVAL(Transform2D()));
ClassDB::bind_method(D_METHOD("area_set_shape", "area", "shape_idx", "shape"), &Physics2DServer::area_set_shape);
ClassDB::bind_method(D_METHOD("area_set_shape_transform", "area", "shape_idx", "transform"), &Physics2DServer::area_set_shape_transform);
+ ClassDB::bind_method(D_METHOD("area_set_shape_disabled", "area", "shape_idx", "disable"), &Physics2DServer::area_set_shape_disabled);
ClassDB::bind_method(D_METHOD("area_get_shape_count", "area"), &Physics2DServer::area_get_shape_count);
ClassDB::bind_method(D_METHOD("area_get_shape", "area", "shape_idx"), &Physics2DServer::area_get_shape);
@@ -539,8 +540,8 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_remove_shape", "body", "shape_idx"), &Physics2DServer::body_remove_shape);
ClassDB::bind_method(D_METHOD("body_clear_shapes", "body"), &Physics2DServer::body_clear_shapes);
- ClassDB::bind_method(D_METHOD("body_set_shape_as_trigger", "body", "shape_idx", "enable"), &Physics2DServer::body_set_shape_as_trigger);
- ClassDB::bind_method(D_METHOD("body_is_shape_set_as_trigger", "body", "shape_idx"), &Physics2DServer::body_is_shape_set_as_trigger);
+ ClassDB::bind_method(D_METHOD("body_set_shape_disabled", "body", "shape_idx", "disable"), &Physics2DServer::body_set_shape_disabled);
+ ClassDB::bind_method(D_METHOD("body_set_shape_as_one_way_collision", "body", "shape_idx", "enable"), &Physics2DServer::body_set_shape_as_one_way_collision);
ClassDB::bind_method(D_METHOD("body_attach_object_instance_ID", "body", "id"), &Physics2DServer::body_attach_object_instance_ID);
ClassDB::bind_method(D_METHOD("body_get_object_instance_ID", "body"), &Physics2DServer::body_get_object_instance_ID);
@@ -571,12 +572,6 @@ void Physics2DServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("body_set_max_contacts_reported", "body", "amount"), &Physics2DServer::body_set_max_contacts_reported);
ClassDB::bind_method(D_METHOD("body_get_max_contacts_reported", "body"), &Physics2DServer::body_get_max_contacts_reported);
- ClassDB::bind_method(D_METHOD("body_set_one_way_collision_direction", "body", "normal"), &Physics2DServer::body_set_one_way_collision_direction);
- ClassDB::bind_method(D_METHOD("body_get_one_way_collision_direction", "body"), &Physics2DServer::body_get_one_way_collision_direction);
-
- ClassDB::bind_method(D_METHOD("body_set_one_way_collision_max_depth", "body", "depth"), &Physics2DServer::body_set_one_way_collision_max_depth);
- ClassDB::bind_method(D_METHOD("body_get_one_way_collision_max_depth", "body"), &Physics2DServer::body_get_one_way_collision_max_depth);
-
ClassDB::bind_method(D_METHOD("body_set_omit_force_integration", "body", "enable"), &Physics2DServer::body_set_omit_force_integration);
ClassDB::bind_method(D_METHOD("body_is_omitting_force_integration", "body"), &Physics2DServer::body_is_omitting_force_integration);
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index 80113dd7d6..f50faa42eb 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -332,6 +332,8 @@ public:
virtual void area_remove_shape(RID p_area, int p_shape_idx) = 0;
virtual void area_clear_shapes(RID p_area) = 0;
+ virtual void area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled) = 0;
+
virtual void area_attach_object_instance_ID(RID p_area, ObjectID p_ID) = 0;
virtual ObjectID area_get_object_instance_ID(RID p_area) const = 0;
@@ -380,8 +382,8 @@ public:
virtual Transform2D body_get_shape_transform(RID p_body, int p_shape_idx) const = 0;
virtual Variant body_get_shape_metadata(RID p_body, int p_shape_idx) const = 0;
- virtual void body_set_shape_as_trigger(RID p_body, int p_shape_idx, bool p_enable) = 0;
- virtual bool body_is_shape_set_as_trigger(RID p_body, int p_shape_idx) const = 0;
+ virtual void body_set_shape_disabled(RID p_body, int p_shape, bool p_disabled) = 0;
+ virtual void body_set_shape_as_one_way_collision(RID p_body, int p_shape, bool p_enabled) = 0;
virtual void body_remove_shape(RID p_body, int p_shape_idx) = 0;
virtual void body_clear_shapes(RID p_body) = 0;
@@ -451,12 +453,6 @@ public:
virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) = 0;
virtual int body_get_max_contacts_reported(RID p_body) const = 0;
- virtual void body_set_one_way_collision_direction(RID p_body, const Vector2 &p_direction) = 0;
- virtual Vector2 body_get_one_way_collision_direction(RID p_body) const = 0;
-
- virtual void body_set_one_way_collision_max_depth(RID p_body, float p_max_depth) = 0;
- virtual float body_get_one_way_collision_max_depth(RID p_body) const = 0;
-
//missing remove
virtual void body_set_contacts_reported_depth_treshold(RID p_body, float p_treshold) = 0;
virtual float body_get_contacts_reported_depth_treshold(RID p_body) const = 0;
@@ -478,6 +474,7 @@ public:
Vector2 collision_point;
Vector2 collision_normal;
Vector2 collider_velocity;
+ int collision_local_shape;
ObjectID collider_id;
RID collider;
int collider_shape;
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 07d6542b62..2ce83e6c64 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -75,6 +75,10 @@ public:
virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
+ virtual bool is_environment(RID p_env) = 0;
+ virtual VS::EnvironmentBG environment_get_background(RID p_env) = 0;
+ virtual int environment_get_canvas_max_layer(RID p_env) = 0;
+
struct InstanceBase : RID_Data {
VS::InstanceType base_type;
@@ -612,6 +616,7 @@ public:
TYPE_POLYGON,
TYPE_MESH,
TYPE_MULTIMESH,
+ TYPE_PARTICLES,
TYPE_CIRCLE,
TYPE_TRANSFORM,
TYPE_CLIP_IGNORE,
@@ -707,6 +712,16 @@ public:
CommandMultiMesh() { type = TYPE_MULTIMESH; }
};
+ struct CommandParticles : public Command {
+
+ RID particles;
+ RID texture;
+ RID normal_map;
+ int h_frames;
+ int v_frames;
+ CommandParticles() { type = TYPE_PARTICLES; }
+ };
+
struct CommandCircle : public Command {
Point2 pos;
@@ -845,6 +860,15 @@ public:
r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
} break;
+ case Item::Command::TYPE_PARTICLES: {
+
+ const Item::CommandParticles *particles_cmd = static_cast<const Item::CommandParticles *>(c);
+ if (particles_cmd->particles.is_valid()) {
+ Rect3 aabb = RasterizerStorage::base_singleton->particles_get_aabb(particles_cmd->particles);
+ r = Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
+ }
+
+ } break;
case Item::Command::TYPE_CIRCLE: {
const Item::CommandCircle *circle = static_cast<const Item::CommandCircle *>(c);
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 81139ecc1c..42f1a98826 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -212,6 +212,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["NUMBER"] = ShaderLanguage::TYPE_UINT;
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["INDEX"] = ShaderLanguage::TYPE_INT;
shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["EMISSION_TRANSFORM"] = ShaderLanguage::TYPE_MAT4;
+ shader_modes[VS::SHADER_PARTICLES].functions["vertex"]["RANDOM_SEED"] = ShaderLanguage::TYPE_UINT;
shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");
diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp
index 2ef1fd417b..1227863b72 100644
--- a/servers/visual/visual_server_canvas.cpp
+++ b/servers/visual/visual_server_canvas.cpp
@@ -58,6 +58,12 @@ void VisualServerCanvas::_render_canvas_item(Item *p_canvas_item, const Transfor
if (!ci->visible)
return;
+ if (p_canvas_item->children_order_dirty) {
+
+ p_canvas_item->child_items.sort_custom<ItemIndexSort>();
+ p_canvas_item->children_order_dirty = false;
+ }
+
Rect2 rect = ci->get_rect();
Transform2D xform = p_transform * ci->xform;
Rect2 global_rect = xform.xform(rect);
@@ -171,6 +177,12 @@ void VisualServerCanvas::render_canvas(Canvas *p_canvas, const Transform2D &p_tr
VSG::canvas_render->canvas_begin();
+ if (p_canvas->children_order_dirty) {
+
+ p_canvas->child_items.sort();
+ p_canvas->children_order_dirty = false;
+ }
+
int l = p_canvas->child_items.size();
Canvas::ChildItem *ci = p_canvas->child_items.ptr();
@@ -637,6 +649,25 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
canvas_item->commands.push_back(m);
}
+void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames) {
+
+ Item *canvas_item = canvas_item_owner.getornull(p_item);
+ ERR_FAIL_COND(!canvas_item);
+
+ Item::CommandParticles *part = memnew(Item::CommandParticles);
+ ERR_FAIL_COND(!part);
+ part->particles = p_particles;
+ part->texture = p_texture;
+ part->normal_map = p_normal;
+ part->h_frames = p_h_frames;
+ part->v_frames = p_v_frames;
+
+ //take the chance and request processing for them, at least once until they become visible again
+ VSG::storage->particles_request_process(p_particles);
+
+ canvas_item->commands.push_back(part);
+}
+
void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h
index 2c86b14c70..57c7515367 100644
--- a/servers/visual/visual_server_canvas.h
+++ b/servers/visual/visual_server_canvas.h
@@ -63,6 +63,14 @@ public:
}
};
+ struct ItemIndexSort {
+
+ _FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
+
+ return p_left->index < p_right->index;
+ }
+ };
+
struct ItemPtrSort {
_FORCE_INLINE_ bool operator()(const Item *p_left, const Item *p_right) const {
@@ -173,6 +181,7 @@ public:
void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID());
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID());
+ void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable);
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index c48bee7fa4..03af60dcd5 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -877,7 +877,8 @@ public:
BIND2(particles_set_draw_passes, RID, int)
BIND3(particles_set_draw_pass_mesh, RID, int, RID)
- BIND1R(Rect3, particles_get_current_aabb, RID);
+ BIND1R(Rect3, particles_get_current_aabb, RID)
+ BIND2(particles_set_emission_transform, RID, const Transform &)
#undef BINDBASE
//from now on, calls forwarded to this singleton
@@ -1049,6 +1050,7 @@ public:
BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
BIND3(canvas_item_add_mesh, RID, const RID &, RID)
BIND3(canvas_item_add_multimesh, RID, RID, RID)
+ BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
BIND2(canvas_item_add_clip_ignore, RID, bool)
BIND2(canvas_item_set_sort_children_by_y, RID, bool)
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index 6d1f698a5c..fb1c66d0b9 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -2162,6 +2162,18 @@ void VisualServerScene::_render_scene(const Transform p_cam_transform, const Cam
VSG::scene_render->render_scene(p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, environment, p_shadow_atlas, scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
}
+void VisualServerScene::render_empty_scene(RID p_scenario, RID p_shadow_atlas) {
+
+ Scenario *scenario = scenario_owner.getornull(p_scenario);
+
+ RID environment;
+ if (scenario->environment.is_valid())
+ environment = scenario->environment;
+ else
+ environment = scenario->fallback_environment;
+ VSG::scene_render->render_scene(Transform(), CameraMatrix(), true, NULL, 0, NULL, 0, NULL, 0, environment, p_shadow_atlas, scenario->reflection_atlas, RID(), 0);
+}
+
bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int p_step) {
InstanceReflectionProbeData *reflection_probe = static_cast<InstanceReflectionProbeData *>(p_instance->base_data);
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index 92c6421987..d13c24ae24 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -519,6 +519,7 @@ public:
_FORCE_INLINE_ void _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_shadow_atlas, Scenario *p_scenario);
void _render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
+ void render_empty_scene(RID p_scenario, RID p_shadow_atlas);
void render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas);
void update_dirty_instances();
diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp
index 2fbbcd225f..2c2bd2b167 100644
--- a/servers/visual/visual_server_viewport.cpp
+++ b/servers/visual/visual_server_viewport.cpp
@@ -35,23 +35,24 @@
void VisualServerViewport::_draw_viewport(Viewport *p_viewport) {
-/* Camera should always be BEFORE any other 3D */
-#if 0
- bool scenario_draw_canvas_bg=false;
- int scenario_canvas_max_layer=0;
+ /* Camera should always be BEFORE any other 3D */
- if (!p_viewport->hide_canvas && !p_viewport->disable_environment && scenario_owner.owns(p_viewport->scenario)) {
+ bool scenario_draw_canvas_bg = false; //draw canvas, or some layer of it, as BG for 3D instead of in front
+ int scenario_canvas_max_layer = 0;
- Scenario *scenario=scenario_owner.get(p_viewport->scenario);
- if (scenario->environment.is_valid()) {
- if (rasterizer->is_environment(scenario->environment)) {
- scenario_draw_canvas_bg=rasterizer->environment_get_background(scenario->environment)==VS::ENV_BG_CANVAS;
- scenario_canvas_max_layer=rasterizer->environment_get_background_param(scenario->environment,VS::ENV_BG_PARAM_CANVAS_MAX_LAYER);
- }
+ if (!p_viewport->hide_canvas && !p_viewport->disable_environment && VSG::scene->scenario_owner.owns(p_viewport->scenario)) {
+
+ VisualServerScene::Scenario *scenario = VSG::scene->scenario_owner.get(p_viewport->scenario);
+ if (VSG::scene_render->is_environment(scenario->environment)) {
+ scenario_draw_canvas_bg = VSG::scene_render->environment_get_background(scenario->environment) == VS::ENV_BG_CANVAS;
+
+ scenario_canvas_max_layer = VSG::scene_render->environment_get_canvas_max_layer(scenario->environment);
}
}
- bool can_draw_3d=!p_viewport->hide_scenario && camera_owner.owns(p_viewport->camera) && scenario_owner.owns(p_viewport->scenario);
+ bool can_draw_3d = !p_viewport->disable_3d && !p_viewport->disable_3d_by_usage && VSG::scene->camera_owner.owns(p_viewport->camera);
+#if 0
+
if (scenario_draw_canvas_bg) {
@@ -88,7 +89,7 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport) {
}
}
- if (!p_viewport->disable_3d && !p_viewport->disable_3d_by_usage && p_viewport->camera.is_valid()) {
+ if (!scenario_draw_canvas_bg && can_draw_3d) {
VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
}
@@ -199,14 +200,15 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport) {
VSG::rasterizer->restore_render_target();
-#if 0
- if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().layer>scenario_canvas_max_layer) {
-
- _draw_viewport_camera(p_viewport,!can_draw_3d);
- scenario_draw_canvas_bg=false;
+ if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().layer > scenario_canvas_max_layer) {
+ if (can_draw_3d) {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ }
+ scenario_draw_canvas_bg = false;
}
-#endif
for (Map<Viewport::CanvasKey, Viewport::CanvasData *>::Element *E = canvas_map.front(); E; E = E->next()) {
@@ -229,19 +231,29 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport) {
VSG::canvas->render_canvas(canvas, xform, canvas_lights, lights_with_mask, clip_rect);
i++;
-#if 0
- if (scenario_draw_canvas_bg && E->key().layer>=scenario_canvas_max_layer) {
- _draw_viewport_camera(p_viewport,!can_draw_3d);
- scenario_draw_canvas_bg=false;
+
+ if (scenario_draw_canvas_bg && E->key().layer >= scenario_canvas_max_layer) {
+
+ if (can_draw_3d) {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ }
+
+ scenario_draw_canvas_bg = false;
}
-#endif
}
-#if 0
+
if (scenario_draw_canvas_bg) {
- _draw_viewport_camera(p_viewport,!can_draw_3d);
- scenario_draw_canvas_bg=false;
+
+ if (can_draw_3d) {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ }
+
+ scenario_draw_canvas_bg = false;
}
-#endif
//VSG::canvas_render->canvas_debug_viewport_shadows(lights_with_shadow);
}
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index 94cbfc815e..79a7805472 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -320,6 +320,7 @@ public:
FUNC2(particles_set_draw_passes, RID, int)
FUNC3(particles_set_draw_pass_mesh, RID, int, RID)
+ FUNC2(particles_set_emission_transform, RID, const Transform &)
FUNC1R(Rect3, particles_get_current_aabb, RID)
@@ -476,6 +477,7 @@ public:
FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
FUNC3(canvas_item_add_mesh, RID, const RID &, RID)
FUNC3(canvas_item_add_multimesh, RID, RID, RID)
+ FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
FUNC2(canvas_item_add_clip_ignore, RID, bool)
FUNC2(canvas_item_set_sort_children_by_y, RID, bool)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 6c57275b3a..c3ae58cf4f 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -137,7 +137,7 @@ void VisualServer::_free_internal_rids() {
if (test_material.is_valid())
free(test_material);
- for (int i = 0; i < 16; i++) {
+ for (int i = 0; i < 32; i++) {
if (material_2d[i].is_valid())
free(material_2d[i]);
}
@@ -284,7 +284,7 @@ RID VisualServer::make_sphere_mesh(int p_lats, int p_lons, float p_radius) {
return mesh;
}
-RID VisualServer::material_2d_get(bool p_shaded, bool p_transparent, bool p_cut_alpha, bool p_opaque_prepass) {
+RID VisualServer::material_2d_get(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass) {
int version = 0;
if (p_shaded)
@@ -295,6 +295,8 @@ RID VisualServer::material_2d_get(bool p_shaded, bool p_transparent, bool p_cut_
version |= 4;
if (p_opaque_prepass)
version |= 8;
+ if (p_double_sided)
+ version |= 16;
if (material_2d[version].is_valid())
return material_2d[version];
@@ -305,7 +307,7 @@ RID VisualServer::material_2d_get(bool p_shaded, bool p_transparent, bool p_cut_
fixed_material_set_flag(material_2d[version],FIXED_MATERIAL_FLAG_USE_COLOR_ARRAY,true);
fixed_material_set_flag(material_2d[version],FIXED_MATERIAL_FLAG_DISCARD_ALPHA,p_cut_alpha);
material_set_flag(material_2d[version],MATERIAL_FLAG_UNSHADED,!p_shaded);
- material_set_flag(material_2d[version],MATERIAL_FLAG_DOUBLE_SIDED,true);
+ material_set_flag(material_2d[version], MATERIAL_FLAG_DOUBLE_SIDED, p_double_sided);
material_set_depth_draw_mode(material_2d[version],p_opaque_prepass?MATERIAL_DEPTH_DRAW_OPAQUE_PRE_PASS_ALPHA:MATERIAL_DEPTH_DRAW_OPAQUE_ONLY);
fixed_material_set_texture(material_2d[version],FIXED_MATERIAL_PARAM_DIFFUSE,get_white_texture());
//material cut alpha?*/
diff --git a/servers/visual_server.h b/servers/visual_server.h
index a27b32f54f..bc72c8a5a1 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -60,7 +60,7 @@ protected:
RID test_texture;
RID white_texture;
RID test_material;
- RID material_2d[16];
+ RID material_2d[32];
Error _surface_set_data(Array p_arrays, uint32_t p_format, uint32_t *p_offsets, uint32_t p_stride, PoolVector<uint8_t> &r_vertex_array, int p_vertex_array_len, PoolVector<uint8_t> &r_index_array, int p_index_array_len, Rect3 &r_aabb, Vector<Rect3> r_bone_aabb);
@@ -501,6 +501,8 @@ public:
virtual Rect3 particles_get_current_aabb(RID p_particles) = 0;
+ virtual void particles_set_emission_transform(RID p_particles, const Transform &p_transform) = 0; //this is only used for 2D, in 3D it's automatic
+
/* CAMERA API */
virtual RID camera_create() = 0;
@@ -793,6 +795,7 @@ public:
virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID()) = 0;
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID()) = 0;
+ virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
virtual void canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) = 0;
@@ -909,7 +912,7 @@ public:
/* Materials for 2D on 3D */
- RID material_2d_get(bool p_shaded, bool p_transparent, bool p_cut_alpha, bool p_opaque_prepass);
+ RID material_2d_get(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass);
/* TESTING */