summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/bind/core_bind.cpp6
-rw-r--r--core/bind/core_bind.h1
-rw-r--r--core/callable.cpp29
-rw-r--r--core/callable.h6
-rw-r--r--core/callable_bind.cpp193
-rw-r--r--core/callable_bind.h85
-rw-r--r--core/core_string_names.cpp2
-rw-r--r--core/core_string_names.h2
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/math/geometry_3d.h48
-rw-r--r--core/math/vector2.h8
-rw-r--r--core/object.cpp14
-rw-r--r--core/object.h1
-rw-r--r--core/thread_work_pool.h27
-rw-r--r--core/variant_call.cpp15
-rw-r--r--doc/classes/Callable.xml14
-rw-r--r--doc/classes/GPUParticles3D.xml2
-rw-r--r--doc/classes/GPUParticlesAttractor3D.xml23
-rw-r--r--doc/classes/GPUParticlesAttractorBox.xml17
-rw-r--r--doc/classes/GPUParticlesAttractorSphere.xml17
-rw-r--r--doc/classes/GPUParticlesAttractorVectorField.xml19
-rw-r--r--doc/classes/GPUParticlesCollision3D.xml17
-rw-r--r--doc/classes/GPUParticlesCollisionBox.xml17
-rw-r--r--doc/classes/GPUParticlesCollisionHeightField.xml43
-rw-r--r--doc/classes/GPUParticlesCollisionSDF.xml37
-rw-r--r--doc/classes/GPUParticlesCollisionSphere.xml17
-rw-r--r--doc/classes/Geometry3D.xml9
-rw-r--r--doc/classes/KinematicBody2D.xml6
-rw-r--r--doc/classes/KinematicBody3D.xml6
-rw-r--r--doc/classes/ParticlesMaterial.xml10
-rw-r--r--doc/classes/RenderingServer.xml14
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_properties.cpp2
-rw-r--r--editor/import/resource_importer_layered_texture.cpp2
-rw-r--r--editor/node_3d_editor_gizmos.cpp261
-rw-r--r--editor/node_3d_editor_gizmos.h17
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp201
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h74
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp9
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp1
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--modules/bullet/rigid_body_bullet.cpp1
-rw-r--r--modules/mono/csharp_script.cpp190
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp1
-rw-r--r--scene/3d/gpu_particles_3d.cpp14
-rw-r--r--scene/3d/gpu_particles_3d.h3
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp901
-rw-r--r--scene/3d/gpu_particles_collision_3d.h342
-rw-r--r--scene/gui/tree.cpp3
-rw-r--r--scene/register_scene_types.cpp10
-rw-r--r--scene/resources/particles_material.cpp98
-rw-r--r--scene/resources/particles_material.h34
-rw-r--r--scene/resources/visual_shader.cpp1
-rw-r--r--servers/rendering/rasterizer.h24
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp1
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp33
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h1
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp17
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_scene_rd.h3
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp418
-rw-r--r--servers/rendering/rasterizer_rd/rasterizer_storage_rd.h100
-rw-r--r--servers/rendering/rasterizer_rd/shaders/particles.glsl333
-rw-r--r--servers/rendering/rendering_server_raster.cpp3
-rw-r--r--servers/rendering/rendering_server_raster.h16
-rw-r--r--servers/rendering/rendering_server_scene.cpp50
-rw-r--r--servers/rendering/rendering_server_scene.h3
-rw-r--r--servers/rendering/rendering_server_wrap_mt.cpp1
-rw-r--r--servers/rendering/rendering_server_wrap_mt.h17
-rw-r--r--servers/rendering/shader_types.cpp5
-rw-r--r--servers/rendering_server.cpp1
-rw-r--r--servers/rendering_server.h39
72 files changed, 3635 insertions, 309 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 88e31c56fe..baf5d4b928 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -1156,10 +1156,6 @@ Vector<Vector3> _Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const
return Geometry3D::clip_polygon(p_points, p_plane);
}
-int _Geometry3D::get_uv84_normal_bit(const Vector3 &p_vector) {
- return Geometry3D::get_uv84_normal_bit(p_vector);
-}
-
void _Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry3D::build_box_planes);
ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry3D::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z));
@@ -1171,8 +1167,6 @@ void _Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &_Geometry3D::get_closest_point_to_segment_uncapped);
- ClassDB::bind_method(D_METHOD("get_uv84_normal_bit", "normal"), &_Geometry3D::get_uv84_normal_bit);
-
ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &_Geometry3D::ray_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &_Geometry3D::segment_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &_Geometry3D::segment_intersects_sphere);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 5c9520d7fe..e75e740cd5 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -344,7 +344,6 @@ public:
Vector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius);
Vector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius);
Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes);
- int get_uv84_normal_bit(const Vector3 &p_vector);
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
diff --git a/core/callable.cpp b/core/callable.cpp
index b7bdc715f8..c368565687 100644
--- a/core/callable.cpp
+++ b/core/callable.cpp
@@ -30,6 +30,7 @@
#include "callable.h"
+#include "callable_bind.h"
#include "core/script_language.h"
#include "message_queue.h"
#include "object.h"
@@ -53,6 +54,18 @@ void Callable::call(const Variant **p_arguments, int p_argcount, Variant &r_retu
}
}
+Callable Callable::bind(const Variant **p_arguments, int p_argcount) const {
+ Vector<Variant> args;
+ args.resize(p_argcount);
+ for (int i = 0; i < p_argcount; i++) {
+ args.write[i] = *p_arguments[i];
+ }
+ return Callable(memnew(CallableCustomBind(*this, args)));
+}
+Callable Callable::unbind(int p_argcount) const {
+ return Callable(memnew(CallableCustomUnbind(*this, p_argcount)));
+}
+
Object *Callable::get_object() const {
if (is_null()) {
return nullptr;
@@ -85,6 +98,18 @@ CallableCustom *Callable::get_custom() const {
return custom;
}
+const Callable *Callable::get_base_comparator() const {
+ const Callable *comparator = nullptr;
+ if (is_custom()) {
+ comparator = custom->get_base_comparator();
+ }
+ if (comparator) {
+ return comparator;
+ } else {
+ return this;
+ }
+}
+
uint32_t Callable::hash() const {
if (is_custom()) {
return custom->hash();
@@ -258,6 +283,10 @@ Callable::~Callable() {
}
}
+const Callable *CallableCustom::get_base_comparator() const {
+ return nullptr;
+}
+
CallableCustom::CallableCustom() {
ref_count.init();
}
diff --git a/core/callable.h b/core/callable.h
index 7fd6b54cf7..936272a681 100644
--- a/core/callable.h
+++ b/core/callable.h
@@ -80,6 +80,9 @@ public:
return method != StringName();
}
+ Callable bind(const Variant **p_arguments, int p_argcount) const;
+ Callable unbind(int p_argcount) const;
+
Object *get_object() const;
ObjectID get_object_id() const;
StringName get_method() const;
@@ -87,6 +90,8 @@ public:
uint32_t hash() const;
+ const Callable *get_base_comparator() const; //used for bind/unbind to do less precise comparisons (ignoring binds) in signal connect/disconnect
+
bool operator==(const Callable &p_callable) const;
bool operator!=(const Callable &p_callable) const;
bool operator<(const Callable &p_callable) const;
@@ -119,6 +124,7 @@ public:
virtual CompareLessFunc get_compare_less_func() const = 0;
virtual ObjectID get_object() const = 0; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0;
+ virtual const Callable *get_base_comparator() const;
CallableCustom();
virtual ~CallableCustom() {}
diff --git a/core/callable_bind.cpp b/core/callable_bind.cpp
new file mode 100644
index 0000000000..da08d3ccbd
--- /dev/null
+++ b/core/callable_bind.cpp
@@ -0,0 +1,193 @@
+/*************************************************************************/
+/* callable_bind.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "callable_bind.h"
+
+//////////////////////////////////
+
+uint32_t CallableCustomBind::hash() const {
+ return callable.hash();
+}
+String CallableCustomBind::get_as_text() const {
+ return callable.operator String();
+}
+
+bool CallableCustomBind::_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomBind *a = (const CallableCustomBind *)p_a;
+ const CallableCustomBind *b = (const CallableCustomBind *)p_b;
+
+ if (!(a->callable != b->callable)) {
+ return false;
+ }
+
+ if (a->binds.size() != b->binds.size()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CallableCustomBind::_less_func(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomBind *a = (const CallableCustomBind *)p_a;
+ const CallableCustomBind *b = (const CallableCustomBind *)p_b;
+
+ if (a->callable < b->callable) {
+ return true;
+ } else if (b->callable < a->callable) {
+ return false;
+ }
+
+ return a->binds.size() < b->binds.size();
+}
+
+CallableCustom::CompareEqualFunc CallableCustomBind::get_compare_equal_func() const {
+ return _equal_func;
+}
+CallableCustom::CompareLessFunc CallableCustomBind::get_compare_less_func() const {
+ return _less_func;
+}
+ObjectID CallableCustomBind::get_object() const {
+ return callable.get_object_id();
+}
+const Callable *CallableCustomBind::get_base_comparator() const {
+ return &callable;
+}
+
+void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
+ for (int i = 0; i < p_argcount; i++) {
+ args[i] = (const Variant *)p_arguments[i];
+ }
+ for (int i = 0; i < binds.size(); i++) {
+ args[i + p_argcount] = (const Variant *)&binds[i];
+ }
+
+ callable.call(args, p_argcount + binds.size(), r_return_value, r_call_error);
+}
+
+CallableCustomBind::CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds) {
+ callable = p_callable;
+ binds = p_binds;
+}
+
+CallableCustomBind::~CallableCustomBind() {
+}
+
+//////////////////////////////////
+
+uint32_t CallableCustomUnbind::hash() const {
+ return callable.hash();
+}
+String CallableCustomUnbind::get_as_text() const {
+ return callable.operator String();
+}
+
+bool CallableCustomUnbind::_equal_func(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomUnbind *a = (const CallableCustomUnbind *)p_a;
+ const CallableCustomUnbind *b = (const CallableCustomUnbind *)p_b;
+
+ if (!(a->callable != b->callable)) {
+ return false;
+ }
+
+ if (a->argcount != b->argcount) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CallableCustomUnbind::_less_func(const CallableCustom *p_a, const CallableCustom *p_b) {
+ const CallableCustomUnbind *a = (const CallableCustomUnbind *)p_a;
+ const CallableCustomUnbind *b = (const CallableCustomUnbind *)p_b;
+
+ if (a->callable < b->callable) {
+ return true;
+ } else if (b->callable < a->callable) {
+ return false;
+ }
+
+ return a->argcount < b->argcount;
+}
+
+CallableCustom::CompareEqualFunc CallableCustomUnbind::get_compare_equal_func() const {
+ return _equal_func;
+}
+CallableCustom::CompareLessFunc CallableCustomUnbind::get_compare_less_func() const {
+ return _less_func;
+}
+ObjectID CallableCustomUnbind::get_object() const {
+ return callable.get_object_id();
+}
+const Callable *CallableCustomUnbind::get_base_comparator() const {
+ return &callable;
+}
+
+void CallableCustomUnbind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ if (argcount > p_argcount) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_call_error.argument = 0;
+ r_call_error.expected = argcount;
+ return;
+ }
+ callable.call(p_arguments, p_argcount - argcount, r_return_value, r_call_error);
+}
+
+CallableCustomUnbind::CallableCustomUnbind(const Callable &p_callable, int p_argcount) {
+ callable = p_callable;
+ argcount = p_argcount;
+}
+
+CallableCustomUnbind::~CallableCustomUnbind() {
+}
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1) {
+ return p_callable.bind((const Variant **)&p_arg1, 1);
+}
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2) {
+ const Variant *args[2] = { &p_arg1, &p_arg2 };
+ return p_callable.bind(args, 2);
+}
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3) {
+ const Variant *args[3] = { &p_arg1, &p_arg2, &p_arg3 };
+ return p_callable.bind(args, 3);
+}
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4) {
+ const Variant *args[4] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4 };
+ return p_callable.bind(args, 4);
+}
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5) {
+ const Variant *args[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 };
+ return p_callable.bind(args, 5);
+}
diff --git a/core/callable_bind.h b/core/callable_bind.h
new file mode 100644
index 0000000000..21b9228be3
--- /dev/null
+++ b/core/callable_bind.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* callable_bind.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CALLABLE_BIND_H
+#define CALLABLE_BIND_H
+
+#include "core/callable.h"
+#include "core/variant.h"
+
+class CallableCustomBind : public CallableCustom {
+ Callable callable;
+ Vector<Variant> binds;
+
+ static bool _equal_func(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool _less_func(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ //for every type that inherits, these must always be the same for this type
+ virtual uint32_t hash() const;
+ virtual String get_as_text() const;
+ virtual CompareEqualFunc get_compare_equal_func() const;
+ virtual CompareLessFunc get_compare_less_func() const;
+ virtual ObjectID get_object() const; //must always be able to provide an object
+ virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const;
+ virtual const Callable *get_base_comparator() const;
+
+ CallableCustomBind(const Callable &p_callable, const Vector<Variant> &p_binds);
+ virtual ~CallableCustomBind();
+};
+
+class CallableCustomUnbind : public CallableCustom {
+ Callable callable;
+ int argcount;
+
+ static bool _equal_func(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool _less_func(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ //for every type that inherits, these must always be the same for this type
+ virtual uint32_t hash() const;
+ virtual String get_as_text() const;
+ virtual CompareEqualFunc get_compare_equal_func() const;
+ virtual CompareLessFunc get_compare_less_func() const;
+ virtual ObjectID get_object() const; //must always be able to provide an object
+ virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const;
+ virtual const Callable *get_base_comparator() const;
+
+ CallableCustomUnbind(const Callable &p_callable, int p_argcount);
+ virtual ~CallableCustomUnbind();
+};
+
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1);
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2);
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3);
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4);
+Callable callable_bind(const Callable &p_callable, const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5);
+
+#endif // CALLABLE_BIND_H
diff --git a/core/core_string_names.cpp b/core/core_string_names.cpp
index 1d3b333efc..6f86f107e6 100644
--- a/core/core_string_names.cpp
+++ b/core/core_string_names.cpp
@@ -73,6 +73,8 @@ CoreStringNames::CoreStringNames() :
a8(StaticCString::create("a8")),
call(StaticCString::create("call")),
call_deferred(StaticCString::create("call_deferred")),
+ bind(StaticCString::create("bind")),
+ unbind(StaticCString::create("unbind")),
emit(StaticCString::create("emit")),
notification(StaticCString::create("notification")) {
}
diff --git a/core/core_string_names.h b/core/core_string_names.h
index 1a18c84572..43597ef301 100644
--- a/core/core_string_names.h
+++ b/core/core_string_names.h
@@ -92,6 +92,8 @@ public:
StringName call;
StringName call_deferred;
+ StringName bind;
+ StringName unbind;
StringName emit;
StringName notification;
};
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 46e45500bf..a25413b21b 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -96,6 +96,10 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) {
ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
+ if (connection == p_connection) {
+ return;
+ }
+
close();
connection = p_connection;
status = STATUS_CONNECTED;
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index 6bbf518141..11cac8f108 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -636,54 +636,6 @@ public:
void optimize_vertices();
};
- _FORCE_INLINE_ static int get_uv84_normal_bit(const Vector3 &p_vector) {
- int lat = Math::fast_ftoi(Math::floor(Math::acos(p_vector.dot(Vector3(0, 1, 0))) * 4.0 / Math_PI + 0.5));
-
- if (lat == 0) {
- return 24;
- } else if (lat == 4) {
- return 25;
- }
-
- int lon = Math::fast_ftoi(Math::floor((Math_PI + Math::atan2(p_vector.x, p_vector.z)) * 8.0 / (Math_PI * 2.0) + 0.5)) % 8;
-
- return lon + (lat - 1) * 8;
- }
-
- _FORCE_INLINE_ static int get_uv84_normal_bit_neighbors(int p_idx) {
- if (p_idx == 24) {
- return 1 | 2 | 4 | 8;
- } else if (p_idx == 25) {
- return (1 << 23) | (1 << 22) | (1 << 21) | (1 << 20);
- } else {
- int ret = 0;
- if ((p_idx % 8) == 0) {
- ret |= (1 << (p_idx + 7));
- } else {
- ret |= (1 << (p_idx - 1));
- }
- if ((p_idx % 8) == 7) {
- ret |= (1 << (p_idx - 7));
- } else {
- ret |= (1 << (p_idx + 1));
- }
-
- int mask = ret | (1 << p_idx);
- if (p_idx < 8) {
- ret |= 24;
- } else {
- ret |= mask >> 8;
- }
-
- if (p_idx >= 16) {
- ret |= 25;
- } else {
- ret |= mask << 8;
- }
-
- return ret;
- }
- }
static MeshData build_convex_mesh(const Vector<Plane> &p_planes);
static Vector<Plane> build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis = Vector3::AXIS_Z);
static Vector<Plane> build_box_planes(const Vector3 &p_extents);
diff --git a/core/math/vector2.h b/core/math/vector2.h
index f41bcc15bc..c2a2656e72 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -65,6 +65,14 @@ struct Vector2 {
real_t length() const;
real_t length_squared() const;
+ Vector2 min(const Vector2 &p_vector2) const {
+ return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y));
+ }
+
+ Vector2 max(const Vector2 &p_vector2) const {
+ return Vector2(MAX(x, p_vector2.x), MAX(y, p_vector2.y));
+ }
+
real_t distance_to(const Vector2 &p_vector2) const;
real_t distance_squared_to(const Vector2 &p_vector2) const;
real_t angle_to(const Vector2 &p_vector2) const;
diff --git a/core/object.cpp b/core/object.cpp
index 67c605c39b..0d9e5c5116 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -1315,9 +1315,10 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co
Callable target = p_callable;
- if (s->slot_map.has(target)) {
+ //compare with the base callable, so binds can be ignored
+ if (s->slot_map.has(*target.get_base_comparator())) {
if (p_flags & CONNECT_REFERENCE_COUNTED) {
- s->slot_map[target].reference_count++;
+ s->slot_map[*target.get_base_comparator()].reference_count++;
return OK;
} else {
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object.");
@@ -1337,7 +1338,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, co
slot.reference_count = 1;
}
- s->slot_map[target] = slot;
+ //use callable version as key, so binds can be ignored
+ s->slot_map[*target.get_base_comparator()] = slot;
return OK;
}
@@ -1364,7 +1366,7 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable
Callable target = p_callable;
- return s->slot_map.has(target);
+ return s->slot_map.has(*target.get_base_comparator());
//const Map<Signal::Target,Signal::Slot>::Element *E = s->slot_map.find(target);
//return (E!=nullptr );
}
@@ -1386,7 +1388,7 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
SignalData *s = signal_map.getptr(p_signal);
ERR_FAIL_COND_MSG(!s, vformat("Nonexistent signal '%s' in %s.", p_signal, to_string()));
- ERR_FAIL_COND_MSG(!s->slot_map.has(p_callable), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
+ ERR_FAIL_COND_MSG(!s->slot_map.has(*p_callable.get_base_comparator()), "Disconnecting nonexistent signal '" + p_signal + "', callable: " + p_callable + ".");
SignalData::Slot *slot = &s->slot_map[p_callable];
@@ -1398,7 +1400,7 @@ void Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
}
target_object->connections.erase(slot->cE);
- s->slot_map.erase(p_callable);
+ s->slot_map.erase(*p_callable.get_base_comparator());
if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
//not user signal, delete
diff --git a/core/object.h b/core/object.h
index 12ef600dfc..f600e597ec 100644
--- a/core/object.h
+++ b/core/object.h
@@ -31,6 +31,7 @@
#ifndef OBJECT_H
#define OBJECT_H
+#include "core/callable_bind.h"
#include "core/hash_map.h"
#include "core/list.h"
#include "core/map.h"
diff --git a/core/thread_work_pool.h b/core/thread_work_pool.h
index e21d3974ee..661060aa3f 100644
--- a/core/thread_work_pool.h
+++ b/core/thread_work_pool.h
@@ -73,13 +73,15 @@ class ThreadWorkPool {
ThreadData *threads = nullptr;
uint32_t thread_count = 0;
+ BaseWork *current_work = nullptr;
static void _thread_function(ThreadData *p_thread);
public:
template <class C, class M, class U>
- void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
+ void begin_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
ERR_FAIL_COND(!threads); //never initialized
+ ERR_FAIL_COND(current_work != nullptr);
index.store(0);
@@ -90,16 +92,37 @@ public:
w->index = &index;
w->max_elements = p_elements;
+ current_work = w;
+
for (uint32_t i = 0; i < thread_count; i++) {
threads[i].work = w;
threads[i].start.post();
}
+ }
+
+ bool is_working() const {
+ return current_work != nullptr;
+ }
+
+ uint32_t get_work_index() const {
+ return index;
+ }
+
+ void end_work() {
+ ERR_FAIL_COND(current_work == nullptr);
for (uint32_t i = 0; i < thread_count; i++) {
threads[i].completed.wait();
threads[i].work = nullptr;
}
- memdelete(w);
+ memdelete(current_work);
+ current_work = nullptr;
+ }
+
+ template <class C, class M, class U>
+ void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
+ begin_work(p_elements, p_instance, p_method, p_userdata);
+ end_work();
}
void init(int p_thread_count = -1);
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 5fd970c8e1..6ee63e1610 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -586,6 +586,7 @@ struct _VariantCall {
VCALL_LOCALMEM0(Callable, get_object_id);
VCALL_LOCALMEM0(Callable, get_method);
VCALL_LOCALMEM0(Callable, hash);
+ VCALL_LOCALMEM1R(Callable, unbind);
VCALL_LOCALMEM0R(Signal, is_null);
VCALL_LOCALMEM0R(Signal, get_object);
@@ -1347,11 +1348,14 @@ void Variant::call_ptr(const StringName &p_method, const Variant **p_args, int p
if (p_method == CoreStringNames::get_singleton()->call) {
reinterpret_cast<const Callable *>(_data._mem)->call(p_args, p_argcount, ret, r_error);
valid = true;
- }
- if (p_method == CoreStringNames::get_singleton()->call_deferred) {
+ } else if (p_method == CoreStringNames::get_singleton()->call_deferred) {
reinterpret_cast<const Callable *>(_data._mem)->call_deferred(p_args, p_argcount);
valid = true;
+ } else if (p_method == CoreStringNames::get_singleton()->bind) {
+ ret = reinterpret_cast<const Callable *>(_data._mem)->bind(p_args, p_argcount);
+ valid = true;
}
+
} else if (type == SIGNAL) {
if (p_method == CoreStringNames::get_singleton()->emit) {
if (r_ret) {
@@ -1696,9 +1700,13 @@ void Variant::get_method_list(List<MethodInfo> *p_list) const {
if (type == CALLABLE) {
MethodInfo mi;
+
+ mi.name = "bind";
+ mi.flags |= METHOD_FLAG_VARARG;
+ p_list->push_back(mi);
+
mi.name = "call";
mi.return_val.usage = PROPERTY_USAGE_NIL_IS_VARIANT;
- mi.flags |= METHOD_FLAG_VARARG;
p_list->push_back(mi);
@@ -2130,6 +2138,7 @@ void register_variant_methods() {
ADDFUNC0R(CALLABLE, INT, Callable, get_object_id, varray());
ADDFUNC0R(CALLABLE, STRING_NAME, Callable, get_method, varray());
ADDFUNC0R(CALLABLE, INT, Callable, hash, varray());
+ ADDFUNC1R(CALLABLE, CALLABLE, Callable, unbind, INT, "argcount", varray());
ADDFUNC0R(SIGNAL, BOOL, Signal, is_null, varray());
ADDFUNC0R(SIGNAL, OBJECT, Signal, get_object, varray());
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index 3bbee993ac..ad5c549fee 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -47,6 +47,12 @@
Creates a new [Callable] for the method called [code]method_name[/code] in the specified [code]object[/code].
</description>
</method>
+ <method name="bind" qualifiers="vararg">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="call" qualifiers="vararg">
<return type="Variant">
</return>
@@ -106,6 +112,14 @@
<description>
</description>
</method>
+ <method name="unbind">
+ <return type="Callable">
+ </return>
+ <argument index="0" name="argcount" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
</constants>
diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml
index e840e457bf..d1296c3418 100644
--- a/doc/classes/GPUParticles3D.xml
+++ b/doc/classes/GPUParticles3D.xml
@@ -67,6 +67,8 @@
<member name="amount" type="int" setter="set_amount" getter="get_amount" default="8">
Number of particles to emit.
</member>
+ <member name="collision_base_size" type="float" setter="set_collision_base_size" getter="get_collision_base_size" default="0.01">
+ </member>
<member name="draw_order" type="int" setter="set_draw_order" getter="get_draw_order" enum="GPUParticles3D.DrawOrder" default="0">
Particle draw order. Uses [enum DrawOrder] values.
</member>
diff --git a/doc/classes/GPUParticlesAttractor3D.xml b/doc/classes/GPUParticlesAttractor3D.xml
new file mode 100644
index 0000000000..111827d294
--- /dev/null
+++ b/doc/classes/GPUParticlesAttractor3D.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesAttractor3D" inherits="VisualInstance3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="attenuation" type="float" setter="set_attenuation" getter="get_attenuation" default="1.0">
+ </member>
+ <member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="4294967295">
+ </member>
+ <member name="directionality" type="float" setter="set_directionality" getter="get_directionality" default="0.0">
+ </member>
+ <member name="strength" type="float" setter="set_strength" getter="get_strength" default="1.0">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesAttractorBox.xml b/doc/classes/GPUParticlesAttractorBox.xml
new file mode 100644
index 0000000000..68616f9bbd
--- /dev/null
+++ b/doc/classes/GPUParticlesAttractorBox.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesAttractorBox" inherits="GPUParticlesAttractor3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesAttractorSphere.xml b/doc/classes/GPUParticlesAttractorSphere.xml
new file mode 100644
index 0000000000..6984427a96
--- /dev/null
+++ b/doc/classes/GPUParticlesAttractorSphere.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesAttractorSphere" inherits="GPUParticlesAttractor3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesAttractorVectorField.xml b/doc/classes/GPUParticlesAttractorVectorField.xml
new file mode 100644
index 0000000000..cf5e375ea3
--- /dev/null
+++ b/doc/classes/GPUParticlesAttractorVectorField.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesAttractorVectorField" inherits="GPUParticlesAttractor3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">
+ </member>
+ <member name="texture" type="Texture3D" setter="set_texture" getter="get_texture">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesCollision3D.xml b/doc/classes/GPUParticlesCollision3D.xml
new file mode 100644
index 0000000000..dce9a32fc4
--- /dev/null
+++ b/doc/classes/GPUParticlesCollision3D.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesCollision3D" inherits="VisualInstance3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="4294967295">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesCollisionBox.xml b/doc/classes/GPUParticlesCollisionBox.xml
new file mode 100644
index 0000000000..17fc124c41
--- /dev/null
+++ b/doc/classes/GPUParticlesCollisionBox.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesCollisionBox" inherits="GPUParticlesCollision3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesCollisionHeightField.xml b/doc/classes/GPUParticlesCollisionHeightField.xml
new file mode 100644
index 0000000000..c6987515a9
--- /dev/null
+++ b/doc/classes/GPUParticlesCollisionHeightField.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesCollisionHeightField" inherits="GPUParticlesCollision3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">
+ </member>
+ <member name="follow_camera_enabled" type="bool" setter="set_follow_camera_mode" getter="is_follow_camera_mode_enabled" default="false">
+ </member>
+ <member name="follow_camera_push_ratio" type="float" setter="set_follow_camera_push_ratio" getter="get_follow_camera_push_ratio" default="0.1">
+ </member>
+ <member name="resolution" type="int" setter="set_resolution" getter="get_resolution" enum="GPUParticlesCollisionHeightField.Resolution" default="2">
+ </member>
+ <member name="update_mode" type="int" setter="set_update_mode" getter="get_update_mode" enum="GPUParticlesCollisionHeightField.UpdateMode" default="0">
+ </member>
+ </members>
+ <constants>
+ <constant name="RESOLUTION_256" value="0" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_512" value="1" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_1024" value="2" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_2048" value="3" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_4096" value="4" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_8192" value="5" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_MAX" value="6" enum="Resolution">
+ </constant>
+ <constant name="UPDATE_MODE_WHEN_MOVED" value="0" enum="UpdateMode">
+ </constant>
+ <constant name="UPDATE_MODE_ALWAYS" value="1" enum="UpdateMode">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesCollisionSDF.xml b/doc/classes/GPUParticlesCollisionSDF.xml
new file mode 100644
index 0000000000..c3cbe4b1c6
--- /dev/null
+++ b/doc/classes/GPUParticlesCollisionSDF.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesCollisionSDF" inherits="GPUParticlesCollision3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3( 1, 1, 1 )">
+ </member>
+ <member name="resolution" type="int" setter="set_resolution" getter="get_resolution" enum="GPUParticlesCollisionSDF.Resolution" default="2">
+ </member>
+ <member name="texture" type="Texture3D" setter="set_texture" getter="get_texture">
+ </member>
+ <member name="thickness" type="float" setter="set_thickness" getter="get_thickness" default="1.0">
+ </member>
+ </members>
+ <constants>
+ <constant name="RESOLUTION_16" value="0" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_32" value="1" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_64" value="2" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_128" value="3" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_256" value="4" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_512" value="5" enum="Resolution">
+ </constant>
+ <constant name="RESOLUTION_MAX" value="6" enum="Resolution">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/GPUParticlesCollisionSphere.xml b/doc/classes/GPUParticlesCollisionSphere.xml
new file mode 100644
index 0000000000..41150960d2
--- /dev/null
+++ b/doc/classes/GPUParticlesCollisionSphere.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GPUParticlesCollisionSphere" inherits="GPUParticlesCollision3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml
index ec685a5ce7..d0b930defb 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -102,15 +102,6 @@
Given the two 3D segments ([code]p1[/code], [code]p2[/code]) and ([code]q1[/code], [code]q2[/code]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([code]p1[/code], [code]p2[/code]) as well the accompanying point on ([code]q1[/code], [code]q2[/code]).
</description>
</method>
- <method name="get_uv84_normal_bit">
- <return type="int">
- </return>
- <argument index="0" name="normal" type="Vector3">
- </argument>
- <description>
- Used internally by the engine.
- </description>
- </method>
<method name="ray_intersects_triangle">
<return type="Variant">
</return>
diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml
index 17c4b85346..425df00b6f 100644
--- a/doc/classes/KinematicBody2D.xml
+++ b/doc/classes/KinematicBody2D.xml
@@ -55,21 +55,21 @@
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_floor" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_wall" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="move_and_collide">
diff --git a/doc/classes/KinematicBody3D.xml b/doc/classes/KinematicBody3D.xml
index 17a6034e9a..a21496de54 100644
--- a/doc/classes/KinematicBody3D.xml
+++ b/doc/classes/KinematicBody3D.xml
@@ -59,21 +59,21 @@
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on the ceiling. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with the ceiling on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_floor" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on the floor. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with the floor on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="is_on_wall" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns [code]true[/code] if the body is on a wall. Only updates when calling [method move_and_slide] or [method move_and_slide_with_snap].
+ Returns [code]true[/code] if the body collided with a wall on the last call of [method move_and_slide] or [method move_and_slide_with_snap]. Otherwise, returns [code]false[/code].
</description>
</method>
<method name="move_and_collide">
diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml
index f6fa3cf38b..77df6245e9 100644
--- a/doc/classes/ParticlesMaterial.xml
+++ b/doc/classes/ParticlesMaterial.xml
@@ -131,6 +131,16 @@
<member name="anim_speed_random" type="float" setter="set_param_randomness" getter="get_param_randomness" default="0.0">
Animation speed randomness ratio.
</member>
+ <member name="attractor_interaction_enabled" type="bool" setter="set_attractor_interaction_enabled" getter="is_attractor_interaction_enabled" default="true">
+ </member>
+ <member name="collision_bounce" type="float" setter="set_collision_bounce" getter="get_collision_bounce" default="0.0">
+ </member>
+ <member name="collision_enabled" type="bool" setter="set_collision_enabled" getter="is_collision_enabled" default="true">
+ </member>
+ <member name="collision_friction" type="float" setter="set_collision_friction" getter="get_collision_friction" default="0.0">
+ </member>
+ <member name="collision_use_scale" type="bool" setter="set_collision_use_scale" getter="is_collision_using_scale" default="false">
+ </member>
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color( 1, 1, 1, 1 )">
Each particle's initial color. If the [GPUParticles2D]'s [code]texture[/code] is defined, it will be multiplied by this color. To have particle display color in a [BaseMaterial3D] make sure to set [member BaseMaterial3D.vertex_color_use_as_albedo] to [code]true[/code].
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 9ca699501f..456022a626 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3579,22 +3579,24 @@
<constant name="INSTANCE_PARTICLES" value="4" enum="InstanceType">
The instance is a particle emitter.
</constant>
- <constant name="INSTANCE_LIGHT" value="5" enum="InstanceType">
+ <constant name="INSTANCE_PARTICLES_COLLISION" value="5" enum="InstanceType">
+ </constant>
+ <constant name="INSTANCE_LIGHT" value="6" enum="InstanceType">
The instance is a light.
</constant>
- <constant name="INSTANCE_REFLECTION_PROBE" value="6" enum="InstanceType">
+ <constant name="INSTANCE_REFLECTION_PROBE" value="7" enum="InstanceType">
The instance is a reflection probe.
</constant>
- <constant name="INSTANCE_DECAL" value="7" enum="InstanceType">
+ <constant name="INSTANCE_DECAL" value="8" enum="InstanceType">
The instance is a decal.
</constant>
- <constant name="INSTANCE_GI_PROBE" value="8" enum="InstanceType">
+ <constant name="INSTANCE_GI_PROBE" value="9" enum="InstanceType">
The instance is a GI probe.
</constant>
- <constant name="INSTANCE_LIGHTMAP" value="9" enum="InstanceType">
+ <constant name="INSTANCE_LIGHTMAP" value="10" enum="InstanceType">
The instance is a lightmap.
</constant>
- <constant name="INSTANCE_MAX" value="10" enum="InstanceType">
+ <constant name="INSTANCE_MAX" value="11" enum="InstanceType">
Represents the size of the [enum InstanceType] enum.
</constant>
<constant name="INSTANCE_GEOMETRY_MASK" value="30" enum="InstanceType">
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 8edda3123b..73562e0695 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -128,6 +128,7 @@
#include "editor/plugins/gi_probe_editor_plugin.h"
#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
+#include "editor/plugins/gpu_particles_collision_sdf_editor_plugin.h"
#include "editor/plugins/gradient_editor_plugin.h"
#include "editor/plugins/item_list_editor_plugin.h"
#include "editor/plugins/light_occluder_2d_editor_plugin.h"
@@ -6635,6 +6636,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(PhysicalBone3DEditorPlugin(this)));
add_editor_plugin(memnew(MeshEditorPlugin(this)));
add_editor_plugin(memnew(MaterialEditorPlugin(this)));
+ add_editor_plugin(memnew(GPUParticlesCollisionSDFEditorPlugin(this)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i, this));
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 4c8af615b4..9e68ef2f35 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -614,7 +614,7 @@ public:
const Ref<InputEventMouseButton> mb = p_ev;
- if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed() && hovered_index > 0) {
// Toggle the flag.
// We base our choice on the hovered flag, so that it always matches the hovered flag.
if (value & (1 << hovered_index)) {
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index bbf62596d0..ac068c05cf 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -51,7 +51,7 @@ String ResourceImporterLayeredTexture::get_importer_name() const {
return "cubemap_array_texture";
} break;
case MODE_3D: {
- return "cubemap_3d_texture";
+ return "3d_texture";
} break;
}
diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp
index 6e5fb6389d..397a958d8f 100644
--- a/editor/node_3d_editor_gizmos.cpp
+++ b/editor/node_3d_editor_gizmos.cpp
@@ -41,6 +41,7 @@
#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_probe.h"
#include "scene/3d/listener_3d.h"
@@ -2455,6 +2456,266 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
////
+////
+
+GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1));
+ create_material("shape_material", gizmo_color);
+ gizmo_color.a = 0.15;
+ create_material("shape_material_internal", gizmo_color);
+
+ create_handle_material("handles");
+}
+
+bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return (Object::cast_to<GPUParticlesCollision3D>(p_spatial) != nullptr) || (Object::cast_to<GPUParticlesAttractor3D>(p_spatial) != nullptr);
+}
+
+String GPUParticlesCollision3DGizmoPlugin::get_name() const {
+ return "GPUParticlesCollision3D";
+}
+
+int GPUParticlesCollision3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+ const Node3D *cs = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ return "Radius";
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ return "Extents";
+ }
+
+ return "";
+}
+
+Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
+ const Node3D *cs = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ return p_gizmo->get_spatial_node()->call("get_radius");
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ return Vector3(p_gizmo->get_spatial_node()->call("get_extents"));
+ }
+
+ return Variant();
+}
+
+void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ Transform gt = sn->get_global_transform();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ float d = ra.x;
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ sn->call("set_radius", d);
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ Vector3 axis;
+ axis[p_idx] = 1.0;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_idx];
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ Vector3 he = sn->call("get_extents");
+ he[p_idx] = d;
+ sn->call("set_extents", he);
+ }
+}
+
+void GPUParticlesCollision3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ if (p_cancel) {
+ sn->call("set_radius", p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Radius"));
+ ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
+ ur->add_undo_method(sn, "set_radius", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ if (p_cancel) {
+ sn->call("set_extents", p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Box Shape Extents"));
+ ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
+ ur->add_undo_method(sn, "set_extents", p_restore);
+ ur->commit_action();
+ }
+}
+
+void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Node3D *cs = p_gizmo->get_spatial_node();
+
+ print_line("redraw request " + itos(cs != nullptr));
+ p_gizmo->clear();
+
+ const Ref<Material> material =
+ get_material("shape_material", p_gizmo);
+ const Ref<Material> material_internal =
+ get_material("shape_material_internal", p_gizmo);
+
+ Ref<Material> handles_material = get_material("handles");
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ float r = cs->call("get_radius");
+
+ Vector<Vector3> points;
+
+ for (int i = 0; i <= 360; i++) {
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
+
+ points.push_back(Vector3(a.x, 0, a.y));
+ points.push_back(Vector3(b.x, 0, b.y));
+ points.push_back(Vector3(0, a.x, a.y));
+ points.push_back(Vector3(0, b.x, b.y));
+ points.push_back(Vector3(a.x, a.y, 0));
+ points.push_back(Vector3(b.x, b.y, 0));
+ }
+
+ Vector<Vector3> collision_segments;
+
+ for (int i = 0; i < 64; i++) {
+ float ra = i * Math_PI * 2.0 / 64.0;
+ float rb = (i + 1) * Math_PI * 2.0 / 64.0;
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y));
+ collision_segments.push_back(Vector3(b.x, 0, b.y));
+ collision_segments.push_back(Vector3(0, a.x, a.y));
+ collision_segments.push_back(Vector3(0, b.x, b.y));
+ collision_segments.push_back(Vector3(a.x, a.y, 0));
+ collision_segments.push_back(Vector3(b.x, b.y, 0));
+ }
+
+ p_gizmo->add_lines(points, material);
+ p_gizmo->add_collision_segments(collision_segments);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r, 0, 0));
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ Vector<Vector3> lines;
+ AABB aabb;
+ aabb.position = -cs->call("get_extents").operator Vector3();
+ aabb.size = aabb.position * -2;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = cs->call("get_extents").operator Vector3()[i];
+ handles.push_back(ax);
+ }
+
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+ p_gizmo->add_handles(handles, handles_material);
+
+ GPUParticlesCollisionSDF *col_sdf = Object::cast_to<GPUParticlesCollisionSDF>(cs);
+ if (col_sdf) {
+ static const int subdivs[GPUParticlesCollisionSDF::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[col_sdf->get_resolution()];
+ float cell_size = aabb.get_longest_axis_size() / subdiv;
+
+ lines.clear();
+
+ for (int i = 1; i < subdiv; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (cell_size * i > aabb.size[j]) {
+ continue;
+ }
+
+ Vector2 dir;
+ dir[j] = 1.0;
+ Vector2 ta, tb;
+ int j_n1 = (j + 1) % 3;
+ int j_n2 = (j + 2) % 3;
+ ta[j_n1] = 1.0;
+ tb[j_n2] = 1.0;
+
+ for (int k = 0; k < 4; k++) {
+ Vector3 from = aabb.position, to = aabb.position;
+ from[j] += cell_size * i;
+ to[j] += cell_size * i;
+
+ if (k & 1) {
+ to[j_n1] += aabb.size[j_n1];
+ } else {
+ to[j_n2] += aabb.size[j_n2];
+ }
+
+ if (k & 2) {
+ from[j_n1] += aabb.size[j_n1];
+ from[j_n2] += aabb.size[j_n2];
+ }
+
+ lines.push_back(from);
+ lines.push_back(to);
+ }
+ }
+ }
+
+ p_gizmo->add_lines(lines, material_internal);
+ }
+ }
+}
+
+/////
+
+////
+
ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h
index c7aae39a45..4826054643 100644
--- a/editor/node_3d_editor_gizmos.h
+++ b/editor/node_3d_editor_gizmos.h
@@ -253,6 +253,23 @@ public:
GPUParticles3DGizmoPlugin();
};
+class GPUParticlesCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(GPUParticlesCollision3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_name() const override;
+ int get_priority() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override;
+ void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override;
+
+ GPUParticlesCollision3DGizmoPlugin();
+};
+
class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
new file mode 100644
index 0000000000..0288645f91
--- /dev/null
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -0,0 +1,201 @@
+/*************************************************************************/
+/* gpu_particles_collision_sdf_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gpu_particles_collision_sdf_editor_plugin.h"
+
+void GPUParticlesCollisionSDFEditorPlugin::_bake() {
+ if (col_sdf) {
+ if (col_sdf->get_texture().is_null() || !col_sdf->get_texture()->get_path().is_resource_file()) {
+ String path = get_tree()->get_edited_scene_root()->get_filename();
+ if (path == String()) {
+ path = "res://" + col_sdf->get_name() + "_data.exr";
+ } else {
+ String ext = path.get_extension();
+ path = path.get_basename() + "." + col_sdf->get_name() + "_data.exr";
+ }
+ probe_file->set_current_path(path);
+ probe_file->popup_file_dialog();
+ return;
+ }
+
+ _sdf_save_path_and_bake(col_sdf->get_texture()->get_path());
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::edit(Object *p_object) {
+ GPUParticlesCollisionSDF *s = Object::cast_to<GPUParticlesCollisionSDF>(p_object);
+ if (!s) {
+ return;
+ }
+
+ col_sdf = s;
+}
+
+bool GPUParticlesCollisionSDFEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("GPUParticlesCollisionSDF");
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PROCESS) {
+ if (!col_sdf) {
+ return;
+ }
+
+ const Vector3i size = col_sdf->get_estimated_cell_size();
+ String text = vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z);
+ int data_size = 2;
+
+ const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
+ text += " - " + vformat(TTR("VRAM Size: %s MB"), String::num(size_mb, 2));
+
+ if (bake_info->get_text() == text) {
+ return;
+ }
+
+ // Color the label depending on the estimated performance level.
+ Color color;
+ if (size_mb <= 16.0 + CMP_EPSILON) {
+ // Fast.
+ color = bake_info->get_theme_color("success_color", "Editor");
+ } else if (size_mb <= 64.0 + CMP_EPSILON) {
+ // Medium.
+ color = bake_info->get_theme_color("warning_color", "Editor");
+ } else {
+ // Slow.
+ color = bake_info->get_theme_color("error_color", "Editor");
+ }
+ bake_info->add_theme_color_override("font_color", color);
+
+ bake_info->set_text(text);
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ bake_hb->show();
+ set_process(true);
+ } else {
+ bake_hb->hide();
+ set_process(false);
+ }
+}
+
+EditorProgress *GPUParticlesCollisionSDFEditorPlugin::tmp_progress = nullptr;
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_begin(int p_steps) {
+ ERR_FAIL_COND(tmp_progress != nullptr);
+
+ tmp_progress = memnew(EditorProgress("bake_sdf", TTR("Bake SDF"), p_steps));
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_step(int p_step, const String &p_description) {
+ ERR_FAIL_COND(tmp_progress == nullptr);
+ tmp_progress->step(p_description, p_step, false);
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_end() {
+ ERR_FAIL_COND(tmp_progress == nullptr);
+ memdelete(tmp_progress);
+ tmp_progress = nullptr;
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake(const String &p_path) {
+ probe_file->hide();
+ if (col_sdf) {
+ Ref<Image> bake_img = col_sdf->bake();
+ if (bake_img.is_null()) {
+ EditorNode::get_singleton()->show_warning("Bake Error.");
+ return;
+ }
+
+ Ref<ConfigFile> config;
+
+ config.instance();
+ if (FileAccess::exists(p_path + ".import")) {
+ config->load(p_path + ".import");
+ }
+
+ config->set_value("remap", "importer", "3d_texture");
+ config->set_value("remap", "type", "StreamTexture3D");
+ if (!config->has_section_key("params", "compress/mode")) {
+ config->set_value("params", "compress/mode", 3); //user may want another compression, so leave it be
+ }
+ config->set_value("params", "compress/channel_pack", 1);
+ config->set_value("params", "mipmaps/generate", false);
+ config->set_value("params", "slices/horizontal", 1);
+ config->set_value("params", "slices/vertical", bake_img->get_meta("depth"));
+
+ config->save(p_path + ".import");
+
+ Error err = bake_img->save_exr(p_path, false);
+ ERR_FAIL_COND(err);
+ ResourceLoader::import(p_path);
+ Ref<Texture> t = ResourceLoader::load(p_path); //if already loaded, it will be updated on refocus?
+ ERR_FAIL_COND(t.is_null());
+
+ col_sdf->set_texture(t);
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_bind_methods() {
+}
+
+GPUParticlesCollisionSDFEditorPlugin::GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ bake_hb = memnew(HBoxContainer);
+ bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bake_hb->hide();
+ bake = memnew(Button);
+ bake->set_flat(true);
+ bake->set_icon(editor->get_gui_base()->get_theme_icon("Bake", "EditorIcons"));
+ bake->set_text(TTR("Bake SDF"));
+ bake->connect("pressed", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_bake));
+ bake_hb->add_child(bake);
+ bake_info = memnew(Label);
+ bake_info->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bake_info->set_clip_text(true);
+ bake_hb->add_child(bake_info);
+
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
+ col_sdf = nullptr;
+ probe_file = memnew(EditorFileDialog);
+ probe_file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ probe_file->add_filter("*.exr");
+ probe_file->connect("file_selected", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake));
+ get_editor_interface()->get_base_control()->add_child(probe_file);
+ probe_file->set_title(TTR("Select path for SDF Texture"));
+
+ GPUParticlesCollisionSDF::bake_begin_function = bake_func_begin;
+ GPUParticlesCollisionSDF::bake_step_function = bake_func_step;
+ GPUParticlesCollisionSDF::bake_end_function = bake_func_end;
+}
+
+GPUParticlesCollisionSDFEditorPlugin::~GPUParticlesCollisionSDFEditorPlugin() {
+}
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
new file mode 100644
index 0000000000..0cdc70a62b
--- /dev/null
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* gpu_particles_collision_sdf_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
+#define GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
+#include "scene/resources/material.h"
+
+class GPUParticlesCollisionSDFEditorPlugin : public EditorPlugin {
+ GDCLASS(GPUParticlesCollisionSDFEditorPlugin, EditorPlugin);
+
+ GPUParticlesCollisionSDF *col_sdf;
+
+ HBoxContainer *bake_hb;
+ Label *bake_info;
+ Button *bake;
+ EditorNode *editor;
+
+ EditorFileDialog *probe_file;
+
+ static EditorProgress *tmp_progress;
+ static void bake_func_begin(int p_steps);
+ static void bake_func_step(int p_step, const String &p_description);
+ static void bake_func_end();
+
+ void _bake();
+ void _sdf_save_path_and_bake(const String &p_path);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual String get_name() const override { return "GPUParticlesCollisionSDF"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node);
+ ~GPUParticlesCollisionSDFEditorPlugin();
+};
+
+#endif // GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b2fd855834..aecbf817c4 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2405,18 +2405,18 @@ void Node3DEditorViewport::_notification(int p_what) {
}
Transform t = sp->get_global_gizmo_transform();
+ VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(sp);
+ AABB new_aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp);
exist = true;
- if (se->last_xform == t && !se->last_xform_dirty) {
+ if (se->last_xform == t && se->aabb == new_aabb && !se->last_xform_dirty) {
continue;
}
changed = true;
se->last_xform_dirty = false;
se->last_xform = t;
- VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(sp);
-
- se->aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp);
+ se->aabb = new_aabb;
t.translate(se->aabb.position);
@@ -6092,6 +6092,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<VehicleWheel3DGizmoPlugin>(memnew(VehicleWheel3DGizmoPlugin)));
add_gizmo_plugin(Ref<VisibilityNotifier3DGizmoPlugin>(memnew(VisibilityNotifier3DGizmoPlugin)));
add_gizmo_plugin(Ref<GPUParticles3DGizmoPlugin>(memnew(GPUParticles3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<GPUParticlesCollision3DGizmoPlugin>(memnew(GPUParticlesCollision3DGizmoPlugin)));
add_gizmo_plugin(Ref<CPUParticles3DGizmoPlugin>(memnew(CPUParticles3DGizmoPlugin)));
add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 1651de4048..2d0f4a19e7 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -3127,6 +3127,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), "light", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Metallic", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic"), "metallic", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness"), "roughness", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular"), "specular", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Transmission", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "transmission"), "transmission", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 75dc840738..fa2270dcd6 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1141,7 +1141,7 @@ void SceneTreeDock::_notification(int p_what) {
node_shortcuts->add_child(button_custom);
button_custom->set_text(TTR("Other Node"));
button_custom->set_icon(get_theme_icon("Add", "EditorIcons"));
- button_custom->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected), make_binds(TOOL_NEW, false));
+ button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false));
node_shortcuts->add_spacer();
create_root_dialog->add_child(node_shortcuts);
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 76c0e0e607..eb599df74c 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -264,6 +264,7 @@ RigidBodyBullet::RigidBodyBullet() :
btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, nullptr, localInertia);
btBody = bulletnew(btRigidBody(cInfo));
+ btBody->setFriction(1.0);
reload_shapes();
setupBulletCollisionObject(btBody);
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a05b38b8bf..70b731d611 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -975,7 +975,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
script->signals_invalidated = true;
script->reload(p_soft_reload);
- script->update_exports();
if (!script->valid) {
script->pending_reload_instances.clear();
@@ -2955,13 +2954,24 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
CRASH_COND(p_script->native == nullptr);
+ p_script->valid = true;
+
+ update_script_class_info(p_script);
+
+#ifdef TOOLS_ENABLED
+ p_script->_update_member_info_no_exports();
+#endif
+}
+
+// Extract information about the script using the mono class.
+void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
GDMonoClass *base = p_script->script_class->get_parent_class();
+ // `base` should only be set if the script is a user defined type.
if (base != p_script->native) {
p_script->base = base;
}
- p_script->valid = true;
p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
if (!p_script->tool) {
@@ -2996,17 +3006,74 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
- // Need to fetch method from base classes as well
+ p_script->rpc_functions.clear();
+ p_script->rpc_variables.clear();
+
GDMonoClass *top = p_script->script_class;
while (top && top != p_script->native) {
+ // Fetch methods from base classes as well
top->fetch_methods_with_godot_api_checks(p_script->native);
+
+ // Update RPC info
+ {
+ Vector<GDMonoMethod *> methods = top->get_all_methods();
+ for (int i = 0; i < methods.size(); i++) {
+ if (!methods[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = methods[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_functions.find(nd)) {
+ p_script->rpc_functions.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ Vector<GDMonoField *> fields = top->get_all_fields();
+ for (int i = 0; i < fields.size(); i++) {
+ if (!fields[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(fields[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = fields[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_variables.find(nd)) {
+ p_script->rpc_variables.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
+ {
+ Vector<GDMonoProperty *> properties = top->get_all_properties();
+ for (int i = 0; i < properties.size(); i++) {
+ if (!properties[i]->is_static()) {
+ MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(properties[i]);
+ if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
+ ScriptNetData nd;
+ nd.name = properties[i]->get_name();
+ nd.mode = mode;
+ if (-1 == p_script->rpc_variables.find(nd)) {
+ p_script->rpc_variables.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
top = top->get_parent_class();
}
+ // Sort so we are 100% that they are always the same.
+ p_script->rpc_functions.sort_custom<SortNetData>();
+ p_script->rpc_variables.sort_custom<SortNetData>();
+
p_script->load_script_signals(p_script->script_class, p_script->native);
-#ifdef TOOLS_ENABLED
- p_script->_update_member_info_no_exports();
-#endif
}
bool CSharpScript::can_instance() const {
@@ -3305,124 +3372,15 @@ Error CSharpScript::reload(bool p_keep_state) {
print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
#endif
- tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
-
- if (!tool) {
- GDMonoClass *nesting_class = script_class->get_nesting_class();
- tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
- }
-
-#if TOOLS_ENABLED
- if (!tool) {
- tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
- }
-#endif
-
native = GDMonoUtils::get_class_native_base(script_class);
CRASH_COND(native == nullptr);
- GDMonoClass *base_class = script_class->get_parent_class();
-
- if (base_class != native) {
- base = base_class;
- }
-
-#ifdef DEBUG_ENABLED
- // For debug builds, we must fetch from all native base methods as well.
- // Native base methods must be fetched before the current class.
- // Not needed if the script class itself is a native class.
+ update_script_class_info(this);
- if (script_class != native) {
- GDMonoClass *native_top = native;
- while (native_top) {
- native_top->fetch_methods_with_godot_api_checks(native);
-
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
-
- native_top = native_top->get_parent_class();
- }
- }
-#endif
-
- script_class->fetch_methods_with_godot_api_checks(native);
-
- // Need to fetch method from base classes as well
- GDMonoClass *top = script_class;
- while (top && top != native) {
- top->fetch_methods_with_godot_api_checks(native);
- top = top->get_parent_class();
- }
-
- load_script_signals(script_class, native);
_update_exports();
}
- rpc_functions.clear();
- rpc_variables.clear();
-
- GDMonoClass *top = script_class;
- while (top && top != native) {
- {
- Vector<GDMonoMethod *> methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); i++) {
- if (!methods[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(methods[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = methods[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_functions.find(nd)) {
- rpc_functions.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoField *> fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- if (!fields[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(fields[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = fields[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoProperty *> properties = top->get_all_properties();
- for (int i = 0; i < properties.size(); i++) {
- if (!properties[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(properties[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = properties[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- top = top->get_parent_class();
- }
-
- // Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
- rpc_variables.sort_custom<SortNetData>();
-
return OK;
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f0b43a40f9..cfe070a371 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -164,6 +164,7 @@ private:
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
+ static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index f13377f3c8..68de686272 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -2088,6 +2088,7 @@ void VisualScriptPropertyGet::_bind_methods() {
BIND_ENUM_CONSTANT(CALL_MODE_SELF);
BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
+ BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
}
class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance {
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index da5c99a873..ec33d7bcab 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -124,6 +124,11 @@ void GPUParticles3D::set_speed_scale(float p_scale) {
RS::get_singleton()->particles_set_speed_scale(particles, p_scale);
}
+void GPUParticles3D::set_collision_base_size(float p_size) {
+ collision_base_size = p_size;
+ RS::get_singleton()->particles_set_collision_base_size(particles, p_size);
+}
+
bool GPUParticles3D::is_emitting() const {
return RS::get_singleton()->particles_get_emitting(particles);
}
@@ -168,6 +173,10 @@ float GPUParticles3D::get_speed_scale() const {
return speed_scale;
}
+float GPUParticles3D::get_collision_base_size() const {
+ return collision_base_size;
+}
+
void GPUParticles3D::set_draw_order(DrawOrder p_order) {
draw_order = p_order;
RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(p_order));
@@ -381,6 +390,7 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles3D::set_fractional_delta);
ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles3D::set_process_material);
ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles3D::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles3D::set_collision_base_size);
ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles3D::is_emitting);
ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles3D::get_amount);
@@ -395,6 +405,7 @@ void GPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles3D::get_fractional_delta);
ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles3D::get_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles3D::get_collision_base_size);
ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &GPUParticles3D::set_draw_order);
@@ -426,6 +437,8 @@ void GPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size");
ADD_GROUP("Drawing", "");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
@@ -469,6 +482,7 @@ GPUParticles3D::GPUParticles3D() {
set_draw_passes(1);
set_draw_order(DRAW_ORDER_INDEX);
set_speed_scale(1);
+ set_collision_base_size(0.01);
}
GPUParticles3D::~GPUParticles3D() {
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index 0d8dadd31d..b68acef21c 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -65,6 +65,7 @@ private:
int fixed_fps;
bool fractional_delta;
NodePath sub_emitter;
+ float collision_base_size;
Ref<Material> process_material;
@@ -94,6 +95,7 @@ public:
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_collision_base_size(float p_ratio);
bool is_emitting() const;
int get_amount() const;
@@ -106,6 +108,7 @@ public:
bool get_use_local_coordinates() const;
Ref<Material> get_process_material() const;
float get_speed_scale() const;
+ float get_collision_base_size() const;
void set_fixed_fps(int p_count);
int get_fixed_fps() const;
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
new file mode 100644
index 0000000000..baaaa23185
--- /dev/null
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -0,0 +1,901 @@
+/*************************************************************************/
+/* gpu_particles_collision_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gpu_particles_collision_3d.h"
+
+#include "core/thread_work_pool.h"
+#include "mesh_instance_3d.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/main/viewport.h"
+
+void GPUParticlesCollision3D::set_cull_mask(uint32_t p_cull_mask) {
+ cull_mask = p_cull_mask;
+ RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);
+}
+
+uint32_t GPUParticlesCollision3D::get_cull_mask() const {
+ return cull_mask;
+}
+
+void GPUParticlesCollision3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesCollision3D::set_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesCollision3D::get_cull_mask);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+}
+
+GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_type) {
+ collision = RS::get_singleton()->particles_collision_create();
+ RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);
+ set_base(collision);
+}
+
+GPUParticlesCollision3D::~GPUParticlesCollision3D() {
+ RS::get_singleton()->free(collision);
+}
+
+/////////////////////////////////
+
+void GPUParticlesCollisionSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
+}
+
+void GPUParticlesCollisionSphere::set_radius(float p_radius) {
+ radius = p_radius;
+ RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
+ update_gizmo();
+}
+
+float GPUParticlesCollisionSphere::get_radius() const {
+ return radius;
+}
+
+AABB GPUParticlesCollisionSphere::get_aabb() const {
+ return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
+}
+
+GPUParticlesCollisionSphere::GPUParticlesCollisionSphere() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) {
+}
+
+GPUParticlesCollisionSphere::~GPUParticlesCollisionSphere() {
+}
+
+///////////////////////////
+
+void GPUParticlesCollisionBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox::get_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+}
+
+void GPUParticlesCollisionBox::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesCollisionBox::get_extents() const {
+ return extents;
+}
+
+AABB GPUParticlesCollisionBox::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionBox::GPUParticlesCollisionBox() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) {
+}
+
+GPUParticlesCollisionBox::~GPUParticlesCollisionBox() {
+}
+
+///////////////////////////////
+///////////////////////////
+
+void GPUParticlesCollisionSDF::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) {
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node);
+ if (mi && mi->is_visible_in_tree()) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (mesh.is_valid()) {
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform();
+
+ if (p_aabb.intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+
+ Node3D *s = Object::cast_to<Node3D>(p_at_node);
+ if (s) {
+ if (s->is_visible_in_tree()) {
+ Array meshes = p_at_node->call("get_meshes");
+ for (int i = 0; i < meshes.size(); i += 2) {
+ Transform mxf = meshes[i];
+ Ref<Mesh> mesh = meshes[i + 1];
+ if (!mesh.is_valid()) {
+ continue;
+ }
+
+ AABB aabb = mesh->get_aabb();
+
+ Transform xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf);
+
+ if (p_aabb.intersects(xf.xform(aabb))) {
+ PlotMesh pm;
+ pm.local_xform = xf;
+ pm.mesh = mesh;
+ plot_meshes.push_back(pm);
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < p_at_node->get_child_count(); i++) {
+ Node *child = p_at_node->get_child(i);
+ _find_meshes(p_aabb, child, plot_meshes);
+ }
+}
+
+uint32_t GPUParticlesCollisionSDF::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) {
+ if (p_face_count == 1) {
+ return BVH::LEAF_BIT | p_faces[0].index;
+ }
+
+ uint32_t index = bvh_tree.size();
+ {
+ BVH bvh;
+
+ for (uint32_t i = 0; i < p_face_count; i++) {
+ const Face3 &f = p_triangles[p_faces[i].index];
+ AABB aabb(f.vertex[0], Vector3());
+ aabb.expand_to(f.vertex[1]);
+ aabb.expand_to(f.vertex[2]);
+ if (p_thickness > 0.0) {
+ Vector3 normal = p_triangles[p_faces[i].index].get_plane().normal;
+ aabb.expand_to(f.vertex[0] - normal * p_thickness);
+ aabb.expand_to(f.vertex[1] - normal * p_thickness);
+ aabb.expand_to(f.vertex[2] - normal * p_thickness);
+ }
+ if (i == 0) {
+ bvh.bounds = aabb;
+ } else {
+ bvh.bounds.merge_with(aabb);
+ }
+ }
+ bvh_tree.push_back(bvh);
+ }
+
+ uint32_t middle = p_face_count / 2;
+
+ SortArray<FacePos, FaceSort> s;
+ s.compare.axis = bvh_tree[index].bounds.get_longest_axis_index();
+ s.sort(p_faces, p_face_count);
+
+ uint32_t left = _create_bvh(bvh_tree, p_faces, middle, p_triangles, p_thickness);
+ uint32_t right = _create_bvh(bvh_tree, p_faces + middle, p_face_count - middle, p_triangles, p_thickness);
+
+ bvh_tree[index].children[0] = left;
+ bvh_tree[index].children[1] = right;
+
+ return index;
+}
+
+static _FORCE_INLINE_ float Vector3_dot2(const Vector3 &p_vec3) {
+ return p_vec3.dot(p_vec3);
+}
+
+void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance) {
+ if (p_bvh_cell & BVH::LEAF_BIT) {
+ p_bvh_cell &= BVH::LEAF_MASK; //remove bit
+
+ Vector3 point = p_pos;
+ Plane p = triangles[p_bvh_cell].get_plane();
+ float d = p.distance_to(point);
+ float inside_d = 1e20;
+ if (d < 0 && d > -thickness) {
+ //inside planes, do this in 2D
+
+ Vector3 x_axis = (triangles[p_bvh_cell].vertex[0] - triangles[p_bvh_cell].vertex[1]).normalized();
+ Vector3 y_axis = p.normal.cross(x_axis).normalized();
+
+ Vector2 points[3];
+ for (int i = 0; i < 3; i++) {
+ points[i] = Vector2(x_axis.dot(triangles[p_bvh_cell].vertex[i]), y_axis.dot(triangles[p_bvh_cell].vertex[i]));
+ }
+
+ Vector2 p2d = Vector2(x_axis.dot(point), y_axis.dot(point));
+
+ {
+ // https://www.shadertoy.com/view/XsXSz4
+
+ Vector2 e0 = points[1] - points[0];
+ Vector2 e1 = points[2] - points[1];
+ Vector2 e2 = points[0] - points[2];
+
+ Vector2 v0 = p2d - points[0];
+ Vector2 v1 = p2d - points[1];
+ Vector2 v2 = p2d - points[2];
+
+ Vector2 pq0 = v0 - e0 * CLAMP(v0.dot(e0) / e0.dot(e0), 0.0, 1.0);
+ Vector2 pq1 = v1 - e1 * CLAMP(v1.dot(e1) / e1.dot(e1), 0.0, 1.0);
+ Vector2 pq2 = v2 - e2 * CLAMP(v2.dot(e2) / e2.dot(e2), 0.0, 1.0);
+
+ float s = SGN(e0.x * e2.y - e0.y * e2.x);
+ Vector2 d2 = Vector2(pq0.dot(pq0), s * (v0.x * e0.y - v0.y * e0.x)).min(Vector2(pq1.dot(pq1), s * (v1.x * e1.y - v1.y * e1.x))).min(Vector2(pq2.dot(pq2), s * (v2.x * e2.y - v2.y * e2.x)));
+
+ inside_d = -Math::sqrt(d2.x) * SGN(d2.y);
+ }
+
+ //make sure distance to planes is not shorter if inside
+ if (inside_d < 0) {
+ inside_d = MAX(inside_d, d);
+ inside_d = MAX(inside_d, -(thickness + d));
+ }
+
+ closest_distance = MIN(closest_distance, inside_d);
+ } else {
+ if (d < 0) {
+ point -= p.normal * thickness; //flatten
+ }
+
+ // https://iquilezles.org/www/articles/distfunctions/distfunctions.htm
+ Vector3 a = triangles[p_bvh_cell].vertex[0];
+ Vector3 b = triangles[p_bvh_cell].vertex[1];
+ Vector3 c = triangles[p_bvh_cell].vertex[2];
+
+ Vector3 ba = b - a;
+ Vector3 pa = point - a;
+ Vector3 cb = c - b;
+ Vector3 pb = point - b;
+ Vector3 ac = a - c;
+ Vector3 pc = point - c;
+ Vector3 nor = ba.cross(ac);
+
+ inside_d = Math::sqrt(
+ (SGN(ba.cross(nor).dot(pa)) +
+ SGN(cb.cross(nor).dot(pb)) +
+ SGN(ac.cross(nor).dot(pc)) <
+ 2.0) ?
+ MIN(MIN(
+ Vector3_dot2(ba * CLAMP(ba.dot(pa) / Vector3_dot2(ba), 0.0, 1.0) - pa),
+ Vector3_dot2(cb * CLAMP(cb.dot(pb) / Vector3_dot2(cb), 0.0, 1.0) - pb)),
+ Vector3_dot2(ac * CLAMP(ac.dot(pc) / Vector3_dot2(ac), 0.0, 1.0) - pc)) :
+ nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor));
+
+ closest_distance = MIN(closest_distance, inside_d);
+ }
+
+ } else {
+ bool pass = true;
+ if (!bvh[p_bvh_cell].bounds.has_point(p_pos)) {
+ //outside, find closest point
+ Vector3 he = bvh[p_bvh_cell].bounds.size * 0.5;
+ Vector3 center = bvh[p_bvh_cell].bounds.position + he;
+
+ Vector3 rel = (p_pos - center).abs();
+ Vector3 closest(MIN(rel.x, he.x), MIN(rel.y, he.y), MIN(rel.z, he.z));
+ float d = rel.distance_to(closest);
+
+ if (d >= closest_distance) {
+ pass = false; //already closer than this aabb, discard
+ }
+ }
+
+ if (pass) {
+ _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[0], triangles, thickness, closest_distance);
+ _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[1], triangles, thickness, closest_distance);
+ }
+ }
+}
+
+void GPUParticlesCollisionSDF::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) {
+ int32_t z_ofs = p_z * params->size.y * params->size.x;
+ for (int32_t y = 0; y < params->size.y; y++) {
+ int32_t y_ofs = z_ofs + y * params->size.x;
+ for (int32_t x = 0; x < params->size.x; x++) {
+ int32_t x_ofs = y_ofs + x;
+ float &cell = params->cells[x_ofs];
+
+ Vector3 pos = params->cell_offset + Vector3(x, y, p_z) * params->cell_size;
+
+ cell = 1e20;
+
+ _find_closest_distance(pos, params->bvh, 0, params->triangles, params->thickness, cell);
+ }
+ }
+}
+
+void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) {
+ ThreadWorkPool work_pool;
+ work_pool.init();
+ work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params);
+ while (work_pool.get_work_index() < (uint32_t)params->size.z) {
+ OS::get_singleton()->delay_usec(10000);
+ bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF");
+ }
+ work_pool.end_work();
+ work_pool.finish();
+}
+
+Vector3i GPUParticlesCollisionSDF::get_estimated_cell_size() const {
+ static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[get_resolution()];
+
+ AABB aabb(-extents, extents * 2);
+
+ float cell_size = aabb.get_longest_axis_size() / float(subdiv);
+
+ Vector3i sdf_size = Vector3i(aabb.size / cell_size);
+ sdf_size.x = MAX(1, sdf_size.x);
+ sdf_size.y = MAX(1, sdf_size.y);
+ sdf_size.z = MAX(1, sdf_size.z);
+ return sdf_size;
+}
+
+Ref<Image> GPUParticlesCollisionSDF::bake() {
+ static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[get_resolution()];
+
+ AABB aabb(-extents, extents * 2);
+
+ float cell_size = aabb.get_longest_axis_size() / float(subdiv);
+
+ Vector3i sdf_size = Vector3i(aabb.size / cell_size);
+ sdf_size.x = MAX(1, sdf_size.x);
+ sdf_size.y = MAX(1, sdf_size.y);
+ sdf_size.z = MAX(1, sdf_size.z);
+
+ if (bake_begin_function) {
+ bake_begin_function(100);
+ }
+
+ aabb.size = Vector3(sdf_size) * cell_size;
+
+ List<PlotMesh> plot_meshes;
+ _find_meshes(aabb, get_parent(), plot_meshes);
+
+ LocalVector<Face3> faces;
+
+ if (bake_step_function) {
+ bake_step_function(0, "Finding Meshes");
+ }
+
+ for (List<PlotMesh>::Element *E = plot_meshes.front(); E; E = E->next()) {
+ const PlotMesh &pm = E->get();
+
+ for (int i = 0; i < pm.mesh->get_surface_count(); i++) {
+ if (pm.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
+ continue; //only triangles
+ }
+
+ Array a = pm.mesh->surface_get_arrays(i);
+
+ Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX];
+ const Vector3 *vr = vertices.ptr();
+ Vector<int> index = a[Mesh::ARRAY_INDEX];
+
+ if (index.size()) {
+ int facecount = index.size() / 3;
+ const int *ir = index.ptr();
+
+ for (int j = 0; j < facecount; j++) {
+ Face3 face;
+
+ for (int k = 0; k < 3; k++) {
+ face.vertex[k] = pm.local_xform.xform(vr[ir[j * 3 + k]]);
+ }
+
+ //test against original bounds
+ if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) {
+ continue;
+ }
+
+ faces.push_back(face);
+ }
+
+ } else {
+ int facecount = vertices.size() / 3;
+
+ for (int j = 0; j < facecount; j++) {
+ Face3 face;
+
+ for (int k = 0; k < 3; k++) {
+ face.vertex[k] = pm.local_xform.xform(vr[j * 3 + k]);
+ }
+
+ //test against original bounds
+ if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) {
+ continue;
+ }
+
+ faces.push_back(face);
+ }
+ }
+ }
+ }
+
+ //compute bvh
+
+ ERR_FAIL_COND_V(faces.size() <= 1, Ref<Image>());
+
+ LocalVector<FacePos> face_pos;
+
+ face_pos.resize(faces.size());
+
+ float th = cell_size * thickness;
+
+ for (uint32_t i = 0; i < faces.size(); i++) {
+ face_pos[i].index = i;
+ face_pos[i].center = (faces[i].vertex[0] + faces[i].vertex[1] + faces[i].vertex[2]) / 2;
+ if (th > 0.0) {
+ face_pos[i].center -= faces[i].get_plane().normal * th * 0.5;
+ }
+ }
+
+ if (bake_step_function) {
+ bake_step_function(0, "Creating BVH");
+ }
+
+ LocalVector<BVH> bvh;
+
+ _create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th);
+
+ Vector<uint8_t> data;
+ data.resize(sdf_size.z * sdf_size.y * sdf_size.x * sizeof(float));
+
+ if (bake_step_function) {
+ bake_step_function(0, "Baking SDF");
+ }
+
+ ComputeSDFParams params;
+ params.cells = (float *)data.ptrw();
+ params.size = sdf_size;
+ params.cell_size = cell_size;
+ params.cell_offset = aabb.position + Vector3(cell_size * 0.5, cell_size * 0.5, cell_size * 0.5);
+ params.bvh = bvh.ptr();
+ params.triangles = faces.ptr();
+ params.thickness = th;
+ _compute_sdf(&params);
+
+ Ref<Image> ret;
+ ret.instance();
+ ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, data);
+ ret->convert(Image::FORMAT_RH); //convert to half, save space
+ ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function
+
+ if (bake_end_function) {
+ bake_end_function();
+ }
+
+ return ret;
+}
+
+void GPUParticlesCollisionSDF::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF::get_resolution);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF::get_texture);
+
+ ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF::set_thickness);
+ ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF::get_thickness);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_thickness", "get_thickness");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
+
+ BIND_ENUM_CONSTANT(RESOLUTION_16);
+ BIND_ENUM_CONSTANT(RESOLUTION_32);
+ BIND_ENUM_CONSTANT(RESOLUTION_64);
+ BIND_ENUM_CONSTANT(RESOLUTION_128);
+ BIND_ENUM_CONSTANT(RESOLUTION_256);
+ BIND_ENUM_CONSTANT(RESOLUTION_512);
+ BIND_ENUM_CONSTANT(RESOLUTION_MAX);
+}
+
+void GPUParticlesCollisionSDF::set_thickness(float p_thickness) {
+ thickness = p_thickness;
+}
+
+float GPUParticlesCollisionSDF::get_thickness() const {
+ return thickness;
+}
+
+void GPUParticlesCollisionSDF::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesCollisionSDF::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesCollisionSDF::set_resolution(Resolution p_resolution) {
+ resolution = p_resolution;
+ update_gizmo();
+}
+
+GPUParticlesCollisionSDF::Resolution GPUParticlesCollisionSDF::get_resolution() const {
+ return resolution;
+}
+
+void GPUParticlesCollisionSDF::set_texture(const Ref<Texture3D> &p_texture) {
+ texture = p_texture;
+ RID tex = texture.is_valid() ? texture->get_rid() : RID();
+ RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
+}
+
+Ref<Texture3D> GPUParticlesCollisionSDF::get_texture() const {
+ return texture;
+}
+
+AABB GPUParticlesCollisionSDF::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionSDF::BakeBeginFunc GPUParticlesCollisionSDF::bake_begin_function = nullptr;
+GPUParticlesCollisionSDF::BakeStepFunc GPUParticlesCollisionSDF::bake_step_function = nullptr;
+GPUParticlesCollisionSDF::BakeEndFunc GPUParticlesCollisionSDF::bake_end_function = nullptr;
+
+GPUParticlesCollisionSDF::GPUParticlesCollisionSDF() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) {
+}
+
+GPUParticlesCollisionSDF::~GPUParticlesCollisionSDF() {
+}
+
+////////////////////////////
+////////////////////////////
+
+void GPUParticlesCollisionHeightField::_notification(int p_what) {
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS) {
+ if (update_mode == UPDATE_MODE_ALWAYS) {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+
+ if (follow_camera_mode && get_viewport()) {
+ Camera3D *cam = get_viewport()->get_camera();
+ if (cam) {
+ Transform xform = get_global_transform();
+ Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized();
+ Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized();
+ float x_len = xform.basis.get_scale().x;
+ float z_len = xform.basis.get_scale().z;
+
+ Vector3 cam_pos = cam->get_global_transform().origin;
+ Transform new_xform = xform;
+
+ while (x_axis.dot(cam_pos - new_xform.origin) > x_len) {
+ new_xform.origin += x_axis * x_len;
+ }
+ while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) {
+ new_xform.origin -= x_axis * x_len;
+ }
+
+ while (z_axis.dot(cam_pos - new_xform.origin) > z_len) {
+ new_xform.origin += z_axis * z_len;
+ }
+ while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) {
+ new_xform.origin -= z_axis * z_len;
+ }
+
+ if (new_xform != xform) {
+ set_global_transform(new_xform);
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+ }
+}
+
+void GPUParticlesCollisionHeightField::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionHeightField::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionHeightField::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField::set_resolution);
+ ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField::get_resolution);
+
+ ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField::set_update_mode);
+ ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField::get_update_mode);
+
+ ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField::set_follow_camera_mode);
+ ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField::set_follow_camera_push_ratio);
+ ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField::get_follow_camera_push_ratio);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode");
+ ADD_GROUP("Folow Camera", "follow_camera_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio");
+
+ BIND_ENUM_CONSTANT(RESOLUTION_256);
+ BIND_ENUM_CONSTANT(RESOLUTION_512);
+ BIND_ENUM_CONSTANT(RESOLUTION_1024);
+ BIND_ENUM_CONSTANT(RESOLUTION_2048);
+ BIND_ENUM_CONSTANT(RESOLUTION_4096);
+ BIND_ENUM_CONSTANT(RESOLUTION_8192);
+ BIND_ENUM_CONSTANT(RESOLUTION_MAX);
+
+ BIND_ENUM_CONSTANT(UPDATE_MODE_WHEN_MOVED);
+ BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS);
+}
+
+void GPUParticlesCollisionHeightField::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) {
+ follow_camera_push_ratio = p_follow_camera_push_ratio;
+}
+
+float GPUParticlesCollisionHeightField::get_follow_camera_push_ratio() const {
+ return follow_camera_push_ratio;
+}
+
+void GPUParticlesCollisionHeightField::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+}
+
+Vector3 GPUParticlesCollisionHeightField::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesCollisionHeightField::set_resolution(Resolution p_resolution) {
+ resolution = p_resolution;
+ RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution));
+ update_gizmo();
+ RS::get_singleton()->particles_collision_height_field_update(_get_collision());
+}
+
+GPUParticlesCollisionHeightField::Resolution GPUParticlesCollisionHeightField::get_resolution() const {
+ return resolution;
+}
+
+void GPUParticlesCollisionHeightField::set_update_mode(UpdateMode p_update_mode) {
+ update_mode = p_update_mode;
+ set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
+}
+
+GPUParticlesCollisionHeightField::UpdateMode GPUParticlesCollisionHeightField::get_update_mode() const {
+ return update_mode;
+}
+
+void GPUParticlesCollisionHeightField::set_follow_camera_mode(bool p_enabled) {
+ follow_camera_mode = p_enabled;
+ set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS);
+}
+
+bool GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled() const {
+ return follow_camera_mode;
+}
+
+AABB GPUParticlesCollisionHeightField::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesCollisionHeightField::GPUParticlesCollisionHeightField() :
+ GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) {
+}
+
+GPUParticlesCollisionHeightField::~GPUParticlesCollisionHeightField() {
+}
+
+////////////////////////////
+////////////////////////////
+
+void GPUParticlesAttractor3D::set_cull_mask(uint32_t p_cull_mask) {
+ cull_mask = p_cull_mask;
+ RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask);
+}
+
+uint32_t GPUParticlesAttractor3D::get_cull_mask() const {
+ return cull_mask;
+}
+
+void GPUParticlesAttractor3D::set_strength(float p_strength) {
+ strength = p_strength;
+ RS::get_singleton()->particles_collision_set_attractor_strength(collision, p_strength);
+}
+
+float GPUParticlesAttractor3D::get_strength() const {
+ return strength;
+}
+
+void GPUParticlesAttractor3D::set_attenuation(float p_attenuation) {
+ attenuation = p_attenuation;
+ RS::get_singleton()->particles_collision_set_attractor_attenuation(collision, p_attenuation);
+}
+
+float GPUParticlesAttractor3D::get_attenuation() const {
+ return attenuation;
+}
+
+void GPUParticlesAttractor3D::set_directionality(float p_directionality) {
+ directionality = p_directionality;
+ RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality);
+ update_gizmo();
+}
+
+float GPUParticlesAttractor3D::get_directionality() const {
+ return directionality;
+}
+
+void GPUParticlesAttractor3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesAttractor3D::set_cull_mask);
+ ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesAttractor3D::get_cull_mask);
+
+ ClassDB::bind_method(D_METHOD("set_strength", "strength"), &GPUParticlesAttractor3D::set_strength);
+ ClassDB::bind_method(D_METHOD("get_strength"), &GPUParticlesAttractor3D::get_strength);
+
+ ClassDB::bind_method(D_METHOD("set_attenuation", "attenuation"), &GPUParticlesAttractor3D::set_attenuation);
+ ClassDB::bind_method(D_METHOD("get_attenuation"), &GPUParticlesAttractor3D::get_attenuation);
+
+ ClassDB::bind_method(D_METHOD("set_directionality", "amount"), &GPUParticlesAttractor3D::set_directionality);
+ ClassDB::bind_method(D_METHOD("get_directionality"), &GPUParticlesAttractor3D::get_directionality);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_lesser"), "set_strength", "get_strength");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "0,8,0.01"), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_directionality", "get_directionality");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
+}
+
+GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type) {
+ collision = RS::get_singleton()->particles_collision_create();
+ RS::get_singleton()->particles_collision_set_collision_type(collision, p_type);
+ set_base(collision);
+}
+GPUParticlesAttractor3D::~GPUParticlesAttractor3D() {
+ RS::get_singleton()->free(collision);
+}
+
+/////////////////////////////////
+
+void GPUParticlesAttractorSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius");
+}
+
+void GPUParticlesAttractorSphere::set_radius(float p_radius) {
+ radius = p_radius;
+ RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius);
+ update_gizmo();
+}
+
+float GPUParticlesAttractorSphere::get_radius() const {
+ return radius;
+}
+
+AABB GPUParticlesAttractorSphere::get_aabb() const {
+ return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2));
+}
+
+GPUParticlesAttractorSphere::GPUParticlesAttractorSphere() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) {
+}
+
+GPUParticlesAttractorSphere::~GPUParticlesAttractorSphere() {
+}
+
+///////////////////////////
+
+void GPUParticlesAttractorBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox::get_extents);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+}
+
+void GPUParticlesAttractorBox::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesAttractorBox::get_extents() const {
+ return extents;
+}
+
+AABB GPUParticlesAttractorBox::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesAttractorBox::GPUParticlesAttractorBox() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) {
+}
+
+GPUParticlesAttractorBox::~GPUParticlesAttractorBox() {
+}
+
+///////////////////////////
+
+void GPUParticlesAttractorVectorField::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorVectorField::set_extents);
+ ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorVectorField::get_extents);
+
+ ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField::set_texture);
+ ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField::get_texture);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture");
+}
+
+void GPUParticlesAttractorVectorField::set_extents(const Vector3 &p_extents) {
+ extents = p_extents;
+ RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents);
+ update_gizmo();
+}
+
+Vector3 GPUParticlesAttractorVectorField::get_extents() const {
+ return extents;
+}
+
+void GPUParticlesAttractorVectorField::set_texture(const Ref<Texture3D> &p_texture) {
+ texture = p_texture;
+ RID tex = texture.is_valid() ? texture->get_rid() : RID();
+ RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex);
+}
+
+Ref<Texture3D> GPUParticlesAttractorVectorField::get_texture() const {
+ return texture;
+}
+
+AABB GPUParticlesAttractorVectorField::get_aabb() const {
+ return AABB(-extents, extents * 2);
+}
+
+GPUParticlesAttractorVectorField::GPUParticlesAttractorVectorField() :
+ GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) {
+}
+
+GPUParticlesAttractorVectorField::~GPUParticlesAttractorVectorField() {
+}
diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h
new file mode 100644
index 0000000000..4c73c7bcb2
--- /dev/null
+++ b/scene/3d/gpu_particles_collision_3d.h
@@ -0,0 +1,342 @@
+/*************************************************************************/
+/* gpu_particles_collision_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GPU_PARTICLES_COLLISION_3D_H
+#define GPU_PARTICLES_COLLISION_3D_H
+
+#include "core/local_vector.h"
+#include "core/rid.h"
+#include "scene/3d/visual_instance_3d.h"
+#include "scene/resources/material.h"
+
+class GPUParticlesCollision3D : public VisualInstance3D {
+ GDCLASS(GPUParticlesCollision3D, VisualInstance3D);
+
+ uint32_t cull_mask = 0xFFFFFFFF;
+ RID collision;
+
+protected:
+ _FORCE_INLINE_ RID _get_collision() { return collision; }
+ static void _bind_methods();
+
+ GPUParticlesCollision3D(RS::ParticlesCollisionType p_type);
+
+public:
+ void set_cull_mask(uint32_t p_cull_mask);
+ uint32_t get_cull_mask() const;
+
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
+
+ ~GPUParticlesCollision3D();
+};
+
+class GPUParticlesCollisionSphere : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSphere, GPUParticlesCollision3D);
+
+ float radius = 1.0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionSphere();
+ ~GPUParticlesCollisionSphere();
+};
+
+class GPUParticlesCollisionBox : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionBox, GPUParticlesCollision3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionBox();
+ ~GPUParticlesCollisionBox();
+};
+
+class GPUParticlesCollisionSDF : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionSDF, GPUParticlesCollision3D);
+
+public:
+ enum Resolution {
+ RESOLUTION_16,
+ RESOLUTION_32,
+ RESOLUTION_64,
+ RESOLUTION_128,
+ RESOLUTION_256,
+ RESOLUTION_512,
+ RESOLUTION_MAX,
+ };
+
+ typedef void (*BakeBeginFunc)(int);
+ typedef void (*BakeStepFunc)(int, const String &);
+ typedef void (*BakeEndFunc)();
+
+private:
+ Vector3 extents = Vector3(1, 1, 1);
+ Resolution resolution = RESOLUTION_64;
+ Ref<Texture3D> texture;
+ float thickness = 1.0;
+
+ struct PlotMesh {
+ Ref<Mesh> mesh;
+ Transform local_xform;
+ };
+
+ void _find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes);
+
+ struct BVH {
+ enum {
+ LEAF_BIT = 1 << 30,
+ LEAF_MASK = LEAF_BIT - 1
+ };
+ AABB bounds;
+ uint32_t children[2];
+ };
+
+ struct FacePos {
+ Vector3 center;
+ uint32_t index;
+ };
+
+ struct FaceSort {
+ uint32_t axis;
+ bool operator()(const FacePos &p_left, const FacePos &p_right) const {
+ return p_left.center[axis] < p_right.center[axis];
+ }
+ };
+
+ uint32_t _create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness);
+
+ struct ComputeSDFParams {
+ float *cells;
+ Vector3i size;
+ float cell_size;
+ Vector3 cell_offset;
+ const BVH *bvh;
+ const Face3 *triangles;
+ float thickness;
+ };
+
+ void _find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance);
+ void _compute_sdf_z(uint32_t p_z, ComputeSDFParams *params);
+ void _compute_sdf(ComputeSDFParams *params);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_thickness(float p_thickness);
+ float get_thickness() const;
+
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_resolution(Resolution p_resolution);
+ Resolution get_resolution() const;
+
+ void set_texture(const Ref<Texture3D> &p_texture);
+ Ref<Texture3D> get_texture() const;
+
+ Vector3i get_estimated_cell_size() const;
+ Ref<Image> bake();
+
+ virtual AABB get_aabb() const override;
+
+ static BakeBeginFunc bake_begin_function;
+ static BakeStepFunc bake_step_function;
+ static BakeEndFunc bake_end_function;
+
+ GPUParticlesCollisionSDF();
+ ~GPUParticlesCollisionSDF();
+};
+
+VARIANT_ENUM_CAST(GPUParticlesCollisionSDF::Resolution)
+
+class GPUParticlesCollisionHeightField : public GPUParticlesCollision3D {
+ GDCLASS(GPUParticlesCollisionHeightField, GPUParticlesCollision3D);
+
+public:
+ enum Resolution {
+ RESOLUTION_256,
+ RESOLUTION_512,
+ RESOLUTION_1024,
+ RESOLUTION_2048,
+ RESOLUTION_4096,
+ RESOLUTION_8192,
+ RESOLUTION_MAX,
+ };
+
+ enum UpdateMode {
+ UPDATE_MODE_WHEN_MOVED,
+ UPDATE_MODE_ALWAYS,
+ };
+
+private:
+ Vector3 extents = Vector3(1, 1, 1);
+ Resolution resolution = RESOLUTION_1024;
+ bool follow_camera_mode = false;
+ float follow_camera_push_ratio = 0.1;
+
+ UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_resolution(Resolution p_resolution);
+ Resolution get_resolution() const;
+
+ void set_update_mode(UpdateMode p_update_mode);
+ UpdateMode get_update_mode() const;
+
+ void set_follow_camera_mode(bool p_enabled);
+ bool is_follow_camera_mode_enabled() const;
+
+ void set_follow_camera_push_ratio(float p_ratio);
+ float get_follow_camera_push_ratio() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesCollisionHeightField();
+ ~GPUParticlesCollisionHeightField();
+};
+
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::Resolution)
+VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::UpdateMode)
+
+class GPUParticlesAttractor3D : public VisualInstance3D {
+ GDCLASS(GPUParticlesAttractor3D, VisualInstance3D);
+
+ uint32_t cull_mask = 0xFFFFFFFF;
+ RID collision;
+ float strength = 1.0;
+ float attenuation = 1.0;
+ float directionality = 0.0;
+
+protected:
+ _FORCE_INLINE_ RID _get_collision() { return collision; }
+ static void _bind_methods();
+
+ GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type);
+
+public:
+ void set_cull_mask(uint32_t p_cull_mask);
+ uint32_t get_cull_mask() const;
+
+ void set_strength(float p_strength);
+ float get_strength() const;
+
+ void set_attenuation(float p_attenuation);
+ float get_attenuation() const;
+
+ void set_directionality(float p_directionality);
+ float get_directionality() const;
+
+ virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); }
+
+ ~GPUParticlesAttractor3D();
+};
+
+class GPUParticlesAttractorSphere : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorSphere, GPUParticlesAttractor3D);
+
+ float radius = 1.0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorSphere();
+ ~GPUParticlesAttractorSphere();
+};
+
+class GPUParticlesAttractorBox : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorBox, GPUParticlesAttractor3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorBox();
+ ~GPUParticlesAttractorBox();
+};
+
+class GPUParticlesAttractorVectorField : public GPUParticlesAttractor3D {
+ GDCLASS(GPUParticlesAttractorVectorField, GPUParticlesAttractor3D);
+
+ Vector3 extents = Vector3(1, 1, 1);
+ Ref<Texture3D> texture;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_extents(const Vector3 &p_extents);
+ Vector3 get_extents() const;
+
+ void set_texture(const Ref<Texture3D> &p_texture);
+ Ref<Texture3D> get_texture() const;
+
+ virtual AABB get_aabb() const override;
+
+ GPUParticlesAttractorVectorField();
+ ~GPUParticlesAttractorVectorField();
+};
+
+#endif // GPU_PARTICLES_COLLISION_3D_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2f5ee20373..318496df70 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1173,6 +1173,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].text.size() > 0) {
float icon_width = p_item->cells[i].get_icon_size().width;
+ if (p_item->get_icon_max_width(i) > 0) {
+ icon_width = p_item->get_icon_max_width(i);
+ }
r.position.x += icon_width;
r.size.x -= icon_width;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 820513c53d..6b602ae6e5 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -194,6 +194,7 @@
#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/3d/immediate_geometry_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_probe.h"
@@ -450,6 +451,15 @@ void register_scene_types() {
ClassDB::register_class<LightmapProbe>();
ClassDB::register_virtual_class<Lightmapper>();
ClassDB::register_class<GPUParticles3D>();
+ ClassDB::register_virtual_class<GPUParticlesCollision3D>();
+ ClassDB::register_class<GPUParticlesCollisionBox>();
+ ClassDB::register_class<GPUParticlesCollisionSphere>();
+ ClassDB::register_class<GPUParticlesCollisionSDF>();
+ ClassDB::register_class<GPUParticlesCollisionHeightField>();
+ ClassDB::register_virtual_class<GPUParticlesAttractor3D>();
+ ClassDB::register_class<GPUParticlesAttractorBox>();
+ ClassDB::register_class<GPUParticlesAttractorSphere>();
+ ClassDB::register_class<GPUParticlesAttractorVectorField>();
ClassDB::register_class<CPUParticles3D>();
ClassDB::register_class<Position3D>();
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index 4bbfa8965a..a286184aee 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -98,6 +98,9 @@ void ParticlesMaterial::init_shaders() {
shader_names->sub_emitter_frequency = "sub_emitter_frequency";
shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
+
+ shader_names->collision_friction = "collision_friction";
+ shader_names->collision_bounce = "collision_bounce";
}
void ParticlesMaterial::finish_shaders() {
@@ -136,6 +139,10 @@ void ParticlesMaterial::_update_shader() {
String code = "shader_type particles;\n";
+ if (collision_scale) {
+ code += "render_mode collision_use_scale;\n";
+ }
+
code += "uniform vec3 direction;\n";
code += "uniform float spread;\n";
code += "uniform float flatness;\n";
@@ -247,6 +254,11 @@ void ParticlesMaterial::_update_shader() {
code += "uniform sampler2D anim_offset_texture;\n";
}
+ if (collision_enabled) {
+ code += "uniform float collision_friction;\n";
+ code += "uniform float collision_bounce;\n";
+ }
+
//need a random function
code += "\n\n";
code += "float rand_from_seed(inout uint seed) {\n";
@@ -476,6 +488,10 @@ void ParticlesMaterial::_update_shader() {
code += " vec3 crossDiff = cross(normalize(diff), normalize(gravity));\n";
code += " force += length(crossDiff) > 0.0 ? normalize(crossDiff) * ((tangent_accel + tex_tangent_accel) * mix(1.0, rand_from_seed(alt_seed), tangent_accel_random)) : vec3(0.0);\n";
}
+ if (attractor_interaction_enabled) {
+ code += " force += ATTRACTOR_FORCE;\n\n";
+ }
+
code += " // apply attractor forces\n";
code += " VELOCITY += force * DELTA;\n";
code += " // orbit velocity\n";
@@ -599,6 +615,13 @@ void ParticlesMaterial::_update_shader() {
code += " VELOCITY.z = 0.0;\n";
code += " TRANSFORM[3].z = 0.0;\n";
}
+ if (collision_enabled) {
+ code += " if (COLLIDED) {\n";
+ code += " TRANSFORM[3].xyz+=COLLISION_NORMAL * COLLISION_DEPTH;\n";
+ code += " VELOCITY -= COLLISION_NORMAL * dot(COLLISION_NORMAL, VELOCITY) * (1.0 + collision_bounce);\n";
+ code += " VELOCITY = mix(VELOCITY,vec3(0.0),collision_friction * DELTA * 100.0);\n";
+ code += " }\n";
+ }
if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
code += " int emit_count = 0;\n";
switch (sub_emitter_mode) {
@@ -609,6 +632,7 @@ void ParticlesMaterial::_update_shader() {
} break;
case SUB_EMITTER_AT_COLLISION: {
//not implemented yet
+ code += " if (COLLIDED) emit_count = 1;\n";
} break;
case SUB_EMITTER_AT_END: {
//not implemented yet
@@ -1072,6 +1096,51 @@ bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
return sub_emitter_keep_velocity;
}
+void ParticlesMaterial::set_attractor_interaction_enabled(bool p_enable) {
+ attractor_interaction_enabled = p_enable;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_attractor_interaction_enabled() const {
+ return attractor_interaction_enabled;
+}
+
+void ParticlesMaterial::set_collision_enabled(bool p_enabled) {
+ collision_enabled = p_enabled;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_enabled() const {
+ return collision_enabled;
+}
+
+void ParticlesMaterial::set_collision_use_scale(bool p_scale) {
+ collision_scale = p_scale;
+ _queue_shader_change();
+}
+
+bool ParticlesMaterial::is_collision_using_scale() const {
+ return collision_scale;
+}
+
+void ParticlesMaterial::set_collision_friction(float p_friction) {
+ collision_friction = p_friction;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_friction, p_friction);
+}
+
+float ParticlesMaterial::get_collision_friction() const {
+ return collision_friction;
+}
+
+void ParticlesMaterial::set_collision_bounce(float p_bounce) {
+ collision_bounce = p_bounce;
+ RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->collision_bounce, p_bounce);
+}
+
+float ParticlesMaterial::get_collision_bounce() const {
+ return collision_bounce;
+}
+
Shader::Mode ParticlesMaterial::get_shader_mode() const {
return Shader::MODE_PARTICLES;
}
@@ -1143,6 +1212,21 @@ void ParticlesMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_sub_emitter_keep_velocity"), &ParticlesMaterial::get_sub_emitter_keep_velocity);
ClassDB::bind_method(D_METHOD("set_sub_emitter_keep_velocity", "enable"), &ParticlesMaterial::set_sub_emitter_keep_velocity);
+ ClassDB::bind_method(D_METHOD("set_attractor_interaction_enabled", "enabled"), &ParticlesMaterial::set_attractor_interaction_enabled);
+ ClassDB::bind_method(D_METHOD("is_attractor_interaction_enabled"), &ParticlesMaterial::is_attractor_interaction_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_enabled", "enabled"), &ParticlesMaterial::set_collision_enabled);
+ ClassDB::bind_method(D_METHOD("is_collision_enabled"), &ParticlesMaterial::is_collision_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collision_use_scale", "radius"), &ParticlesMaterial::set_collision_use_scale);
+ ClassDB::bind_method(D_METHOD("is_collision_using_scale"), &ParticlesMaterial::is_collision_using_scale);
+
+ ClassDB::bind_method(D_METHOD("set_collision_friction", "friction"), &ParticlesMaterial::set_collision_friction);
+ ClassDB::bind_method(D_METHOD("get_collision_friction"), &ParticlesMaterial::get_collision_friction);
+
+ ClassDB::bind_method(D_METHOD("set_collision_bounce", "bounce"), &ParticlesMaterial::set_collision_bounce);
+ ClassDB::bind_method(D_METHOD("get_collision_bounce"), &ParticlesMaterial::get_collision_bounce);
+
ADD_GROUP("Time", "");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
@@ -1221,6 +1305,14 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
+ ADD_GROUP("Attractor Interaction", "attractor_interaction_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "attractor_interaction_enabled"), "set_attractor_interaction_enabled", "is_attractor_interaction_enabled");
+ ADD_GROUP("Collision", "collision_");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_enabled"), "set_collision_enabled", "is_collision_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_friction", "get_collision_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_collision_bounce", "get_collision_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_use_scale"), "set_collision_use_scale", "is_collision_using_scale");
+
BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
@@ -1283,6 +1375,12 @@ ParticlesMaterial::ParticlesMaterial() :
set_sub_emitter_amount_at_end(1);
set_sub_emitter_keep_velocity(false);
+ set_attractor_interaction_enabled(true);
+ set_collision_enabled(true);
+ set_collision_bounce(0.0);
+ set_collision_friction(0.0);
+ set_collision_use_scale(false);
+
for (int i = 0; i < PARAM_MAX; i++) {
set_param_randomness(Parameter(i), 0);
}
diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h
index fa8858f67f..7aca708889 100644
--- a/scene/resources/particles_material.h
+++ b/scene/resources/particles_material.h
@@ -37,11 +37,7 @@
/*
TODO:
-Path following
-*Manual emission
--Sub Emitters
--Attractors
-Emitter positions deformable by bones
--Collision
-Proper trails
*/
@@ -99,6 +95,9 @@ private:
uint32_t invalid_key : 1;
uint32_t has_emission_color : 1;
uint32_t sub_emitter : 2;
+ uint32_t attractor_enabled : 1;
+ uint32_t collision_enabled : 1;
+ uint32_t collision_scale : 1;
};
uint32_t key;
@@ -135,6 +134,9 @@ private:
mk.emission_shape = emission_shape;
mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
mk.sub_emitter = sub_emitter_mode;
+ mk.collision_enabled = collision_enabled;
+ mk.attractor_enabled = attractor_interaction_enabled;
+ mk.collision_scale = collision_scale;
return mk;
}
@@ -201,6 +203,9 @@ private:
StringName sub_emitter_frequency;
StringName sub_emitter_amount_at_end;
StringName sub_emitter_keep_velocity;
+
+ StringName collision_friction;
+ StringName collision_bounce;
};
static ShaderNames *shader_names;
@@ -244,6 +249,12 @@ private:
bool sub_emitter_keep_velocity;
//do not save emission points here
+ bool attractor_interaction_enabled;
+ bool collision_enabled;
+ bool collision_scale;
+ float collision_friction;
+ float collision_bounce;
+
protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
@@ -298,6 +309,21 @@ public:
void set_lifetime_randomness(float p_lifetime);
float get_lifetime_randomness() const;
+ void set_attractor_interaction_enabled(bool p_enable);
+ bool is_attractor_interaction_enabled() const;
+
+ void set_collision_enabled(bool p_enabled);
+ bool is_collision_enabled() const;
+
+ void set_collision_use_scale(bool p_scale);
+ bool is_collision_using_scale() const;
+
+ void set_collision_friction(float p_friction);
+ float get_collision_friction() const;
+
+ void set_collision_bounce(float p_bounce);
+ float get_collision_bounce() const;
+
static void init_shaders();
static void finish_shaders();
static void flush_changes();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 84437faca0..1afc4e114e 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1712,6 +1712,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_camera", "INV_CAMERA_MATRIX" },
diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h
index a24189bdd7..84c04f34b6 100644
--- a/servers/rendering/rasterizer.h
+++ b/servers/rendering/rasterizer.h
@@ -299,6 +299,7 @@ public:
virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void render_sdfgi(RID p_render_buffers, int p_region, InstanceBase **p_cull_result, int p_cull_count) = 0;
virtual void render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const RID **p_positional_light_cull_result, const uint32_t *p_positional_light_cull_count) = 0;
+ virtual void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, InstanceBase **p_cull_result, int p_cull_count) = 0;
virtual void set_scene_pass(uint64_t p_pass) = 0;
virtual void set_time(double p_time, double p_step) = 0;
@@ -660,6 +661,7 @@ public:
virtual void particles_set_process_material(RID p_particles, RID p_material) = 0;
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0;
+ virtual void particles_set_collision_base_size(RID p_particles, float p_size) = 0;
virtual void particles_restart(RID p_particles) = 0;
virtual void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) = 0;
virtual void particles_set_subemitter(RID p_particles, RID p_subemitter_particles) = 0;
@@ -682,6 +684,28 @@ public:
virtual void particles_set_view_axis(RID p_particles, const Vector3 &p_axis) = 0;
+ virtual void particles_add_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance) = 0;
+ virtual void particles_remove_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance) = 0;
+
+ virtual void update_particles() = 0;
+
+ /* PARTICLES COLLISION */
+
+ virtual RID particles_collision_create() = 0;
+ virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) = 0;
+ virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) = 0;
+ virtual void particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius) = 0; //for spheres
+ virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) = 0; //for non-spheres
+ virtual void particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength) = 0;
+ virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) = 0;
+ virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve) = 0;
+ virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) = 0; //for SDF and vector field, heightfield is dynamic
+ virtual void particles_collision_height_field_update(RID p_particles_collision) = 0; //for SDF and vector field
+ virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) = 0; //for SDF and vector field
+ virtual AABB particles_collision_get_aabb(RID p_particles_collision) const = 0;
+ virtual bool particles_collision_is_heightfield(RID p_particles_collision) const = 0;
+ virtual RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const = 0;
+
/* GLOBAL VARIABLES */
virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) = 0;
diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
index 527ed09584..71acd4ceb6 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
@@ -1758,6 +1758,7 @@ RasterizerEffectsRD::~RasterizerEffectsRD() {
resolve.shader.version_free(resolve.shader_version);
roughness.shader.version_free(roughness.shader_version);
roughness_limiter.shader.version_free(roughness_limiter.shader_version);
+ sort.shader.version_free(sort.shader_version);
specular_merge.shader.version_free(specular_merge.shader_version);
ssao.blur_shader.version_free(ssao.blur_shader_version);
ssao.gather_shader.version_free(ssao.gather_shader_version);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
index c56c208098..ac028e93f1 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
@@ -2018,6 +2018,39 @@ void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase **
}
}
+void RasterizerSceneHighEndRD::_render_particle_collider_heightfield(RID p_fb, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, InstanceBase **p_cull_result, int p_cull_count) {
+ RENDER_TIMESTAMP("Setup Render Collider Heightfield");
+
+ _update_render_base_uniform_set();
+
+ render_pass++;
+
+ scene_state.ubo.dual_paraboloid_side = 0;
+
+ _setup_environment(RID(), RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), true, Color(), 0, p_cam_projection.get_z_far(), false, false);
+
+ render_list.clear();
+
+ PassMode pass_mode = PASS_MODE_SHADOW;
+
+ _fill_render_list(p_cull_result, p_cull_count, pass_mode);
+
+ _setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0);
+
+ RENDER_TIMESTAMP("Render Collider Heightield");
+
+ render_list.sort_by_key(false);
+
+ _fill_instances(render_list.elements, render_list.element_count, true);
+
+ {
+ //regular forward for now
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
+ _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_fb), render_list.elements, render_list.element_count, false, pass_mode, true, RID(), RID());
+ RD::get_singleton()->draw_list_end();
+ }
+}
+
void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) {
RENDER_TIMESTAMP("Setup Rendering Material");
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
index 1aad9039ff..4c89928c95 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
@@ -581,6 +581,7 @@ protected:
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, InstanceBase **p_cull_result, int p_cull_count, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture);
+ virtual void _render_particle_collider_heightfield(RID p_fb, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, InstanceBase **p_cull_result, int p_cull_count);
public:
virtual void set_time(double p_time, double p_step);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
index 958d8eac1f..934330cc9b 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
@@ -7511,6 +7511,23 @@ void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, Instanc
}
}
+void RasterizerSceneRD::render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, InstanceBase **p_cull_result, int p_cull_count) {
+ ERR_FAIL_COND(!storage->particles_collision_is_heightfield(p_collider));
+ Vector3 extents = storage->particles_collision_get_extents(p_collider) * p_transform.basis.get_scale();
+ CameraMatrix cm;
+ cm.set_orthogonal(-extents.x, extents.x, -extents.z, extents.z, 0, extents.y * 2.0);
+
+ Vector3 cam_pos = p_transform.origin;
+ cam_pos.y += extents.y;
+
+ Transform cam_xform;
+ cam_xform.set_look_at(cam_pos, cam_pos - p_transform.basis.get_axis(Vector3::AXIS_Y), -p_transform.basis.get_axis(Vector3::AXIS_Z).normalized());
+
+ RID fb = storage->particles_collision_get_heightfield_framebuffer(p_collider);
+
+ _render_particle_collider_heightfield(fb, cam_xform, cm, p_cull_result, p_cull_count);
+}
+
void RasterizerSceneRD::render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const RID **p_positional_light_cull_result, const uint32_t *p_positional_light_cull_count) {
RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
ERR_FAIL_COND(!rb);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
index fe31d2f76b..0e7e56716b 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
@@ -112,6 +112,7 @@ protected:
virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, InstanceBase **p_cull_result, int p_cull_count, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0;
+ virtual void _render_particle_collider_heightfield(RID p_fb, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, InstanceBase **p_cull_result, int p_cull_count) = 0;
virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
@@ -1876,6 +1877,8 @@ public:
void render_sdfgi(RID p_render_buffers, int p_region, InstanceBase **p_cull_result, int p_cull_count);
void render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const RID **p_positional_light_cull_result, const uint32_t *p_positional_light_cull_count);
+ void render_particle_collider_heightfield(RID p_collider, const Transform &p_transform, InstanceBase **p_cull_result, int p_cull_count);
+
virtual void set_scene_pass(uint64_t p_pass) {
scene_pass = p_pass;
}
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
index db2c707984..90dd6af319 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
@@ -3333,6 +3333,10 @@ void RasterizerStorageRD::_particles_free_data(Particles *particles) {
particles->particles_transforms_buffer_uniform_set = RID();
particles->particle_buffer = RID();
+ if (RD::get_singleton()->uniform_set_is_valid(particles->collision_textures_uniform_set)) {
+ RD::get_singleton()->free(particles->collision_textures_uniform_set);
+ }
+
if (particles->particles_sort_buffer.is_valid()) {
RD::get_singleton()->free(particles->particles_sort_buffer);
particles->particles_sort_buffer = RID();
@@ -3454,6 +3458,13 @@ void RasterizerStorageRD::particles_set_fractional_delta(RID p_particles, bool p
particles->fractional_delta = p_enable;
}
+void RasterizerStorageRD::particles_set_collision_base_size(RID p_particles, float p_size) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+
+ particles->collision_base_size = p_size;
+}
+
void RasterizerStorageRD::particles_set_process_material(RID p_particles, RID p_material) {
Particles *particles = particles_owner.getornull(p_particles);
ERR_FAIL_COND(!particles);
@@ -3646,6 +3657,22 @@ RID RasterizerStorageRD::particles_get_draw_pass_mesh(RID p_particles, int p_pas
return particles->draw_passes[p_pass];
}
+void RasterizerStorageRD::particles_add_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+
+ ERR_FAIL_COND(p_instance->base_type != RS::INSTANCE_PARTICLES_COLLISION);
+
+ particles->collisions.insert(p_instance);
+}
+
+void RasterizerStorageRD::particles_remove_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance) {
+ Particles *particles = particles_owner.getornull(p_particles);
+ ERR_FAIL_COND(!particles);
+
+ particles->collisions.erase(p_instance);
+}
+
void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_delta) {
if (p_particles->particles_material_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(p_particles->particles_material_uniform_set)) {
Vector<RD::Uniform> uniforms;
@@ -3729,6 +3756,195 @@ void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_del
frame_params.cycle = p_particles->cycle_number;
+ { //collision and attractors
+
+ frame_params.collider_count = 0;
+ frame_params.attractor_count = 0;
+ frame_params.particle_size = p_particles->collision_base_size;
+
+ RID collision_3d_textures[ParticlesFrameParams::MAX_3D_TEXTURES];
+ RID collision_heightmap_texture;
+
+ Transform to_particles;
+ if (p_particles->use_local_coords) {
+ to_particles = p_particles->emission_transform.affine_inverse();
+ }
+ uint32_t collision_3d_textures_used = 0;
+ for (const Set<RasterizerScene::InstanceBase *>::Element *E = p_particles->collisions.front(); E; E = E->next()) {
+ ParticlesCollision *pc = particles_collision_owner.getornull(E->get()->base);
+ Transform to_collider = E->get()->transform;
+ if (p_particles->use_local_coords) {
+ to_collider = to_particles * to_collider;
+ }
+ Vector3 scale = to_collider.basis.get_scale();
+ to_collider.basis.orthonormalize();
+
+ if (pc->type <= RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) {
+ //attractor
+ if (frame_params.attractor_count >= ParticlesFrameParams::MAX_ATTRACTORS) {
+ continue;
+ }
+
+ ParticlesFrameParams::Attractor &attr = frame_params.attractors[frame_params.attractor_count];
+
+ store_transform(to_collider, attr.transform);
+ attr.strength = pc->attractor_strength;
+ attr.attenuation = pc->attractor_attenuation;
+ attr.directionality = pc->attractor_directionality;
+
+ switch (pc->type) {
+ case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT: {
+ attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_SPHERE;
+ float radius = pc->radius;
+ radius *= (scale.x + scale.y + scale.z) / 3.0;
+ attr.extents[0] = radius;
+ attr.extents[1] = radius;
+ attr.extents[2] = radius;
+ } break;
+ case RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT: {
+ attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_BOX;
+ Vector3 extents = pc->extents * scale;
+ attr.extents[0] = extents.x;
+ attr.extents[1] = extents.y;
+ attr.extents[2] = extents.z;
+ } break;
+ case RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT: {
+ if (collision_3d_textures_used >= ParticlesFrameParams::MAX_3D_TEXTURES) {
+ continue;
+ }
+ attr.type = ParticlesFrameParams::ATTRACTOR_TYPE_VECTOR_FIELD;
+ Vector3 extents = pc->extents * scale;
+ attr.extents[0] = extents.x;
+ attr.extents[1] = extents.y;
+ attr.extents[2] = extents.z;
+ attr.texture_index = collision_3d_textures_used;
+
+ collision_3d_textures[collision_3d_textures_used] = pc->field_texture;
+ collision_3d_textures_used++;
+ } break;
+ default: {
+ }
+ }
+
+ frame_params.attractor_count++;
+ } else {
+ //collider
+ if (frame_params.collider_count >= ParticlesFrameParams::MAX_COLLIDERS) {
+ continue;
+ }
+
+ ParticlesFrameParams::Collider &col = frame_params.colliders[frame_params.collider_count];
+
+ store_transform(to_collider, col.transform);
+ switch (pc->type) {
+ case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: {
+ col.type = ParticlesFrameParams::COLLISION_TYPE_SPHERE;
+ float radius = pc->radius;
+ radius *= (scale.x + scale.y + scale.z) / 3.0;
+ col.extents[0] = radius;
+ col.extents[1] = radius;
+ col.extents[2] = radius;
+ } break;
+ case RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE: {
+ col.type = ParticlesFrameParams::COLLISION_TYPE_BOX;
+ Vector3 extents = pc->extents * scale;
+ col.extents[0] = extents.x;
+ col.extents[1] = extents.y;
+ col.extents[2] = extents.z;
+ } break;
+ case RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE: {
+ if (collision_3d_textures_used >= ParticlesFrameParams::MAX_3D_TEXTURES) {
+ continue;
+ }
+ col.type = ParticlesFrameParams::COLLISION_TYPE_SDF;
+ Vector3 extents = pc->extents * scale;
+ col.extents[0] = extents.x;
+ col.extents[1] = extents.y;
+ col.extents[2] = extents.z;
+ col.texture_index = collision_3d_textures_used;
+ col.scale = (scale.x + scale.y + scale.z) * 0.333333333333; //non uniform scale non supported
+
+ collision_3d_textures[collision_3d_textures_used] = pc->field_texture;
+ collision_3d_textures_used++;
+ } break;
+ case RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE: {
+ if (collision_heightmap_texture != RID()) { //already taken
+ continue;
+ }
+
+ col.type = ParticlesFrameParams::COLLISION_TYPE_HEIGHT_FIELD;
+ Vector3 extents = pc->extents * scale;
+ col.extents[0] = extents.x;
+ col.extents[1] = extents.y;
+ col.extents[2] = extents.z;
+ collision_heightmap_texture = pc->heightfield_texture;
+ } break;
+ default: {
+ }
+ }
+
+ frame_params.collider_count++;
+ }
+ }
+
+ bool different = false;
+ if (collision_3d_textures_used == p_particles->collision_3d_textures_used) {
+ for (int i = 0; i < ParticlesFrameParams::MAX_3D_TEXTURES; i++) {
+ if (p_particles->collision_3d_textures[i] != collision_3d_textures[i]) {
+ different = true;
+ break;
+ }
+ }
+ }
+
+ if (collision_heightmap_texture != p_particles->collision_heightmap_texture) {
+ different = true;
+ }
+
+ bool uniform_set_valid = RD::get_singleton()->uniform_set_is_valid(p_particles->collision_textures_uniform_set);
+
+ if (different || !uniform_set_valid) {
+ if (uniform_set_valid) {
+ RD::get_singleton()->free(p_particles->collision_textures_uniform_set);
+ }
+
+ Vector<RD::Uniform> uniforms;
+
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 0;
+ for (uint32_t i = 0; i < ParticlesFrameParams::MAX_3D_TEXTURES; i++) {
+ RID rd_tex;
+ if (i < collision_3d_textures_used) {
+ Texture *t = texture_owner.getornull(collision_3d_textures[i]);
+ if (t && t->type == Texture::TYPE_3D) {
+ rd_tex = t->rd_texture;
+ }
+ }
+
+ if (rd_tex == RID()) {
+ rd_tex = default_rd_textures[DEFAULT_RD_TEXTURE_3D_WHITE];
+ }
+ u.ids.push_back(rd_tex);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 1;
+ if (collision_heightmap_texture.is_valid()) {
+ u.ids.push_back(collision_heightmap_texture);
+ } else {
+ u.ids.push_back(default_rd_textures[DEFAULT_RD_TEXTURE_BLACK]);
+ }
+ uniforms.push_back(u);
+ }
+ p_particles->collision_textures_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 2);
+ }
+ }
+
ParticlesShader::PushConstant push_constant;
push_constant.clear = p_particles->clear;
@@ -3783,8 +3999,10 @@ void RasterizerStorageRD::_particles_process(Particles *p_particles, float p_del
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, m->shader_data->pipeline);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, particles_shader.base_uniform_set, 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, 2);
+
if (m->uniform_set.is_valid()) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 2);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3);
}
RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ParticlesShader::PushConstant));
@@ -4190,7 +4408,7 @@ void RasterizerStorageRD::ParticlesMaterialData::update_parameters(const Map<Str
}
}
- uniform_set = RD::get_singleton()->uniform_set_create(uniforms, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 2);
+ uniform_set = RD::get_singleton()->uniform_set_create(uniforms, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 3);
}
RasterizerStorageRD::ParticlesMaterialData::~ParticlesMaterialData() {
@@ -4211,6 +4429,171 @@ RasterizerStorageRD::MaterialData *RasterizerStorageRD::_create_particles_materi
return material_data;
}
////////
+
+/* PARTICLES COLLISION API */
+
+RID RasterizerStorageRD::particles_collision_create() {
+ return particles_collision_owner.make_rid(ParticlesCollision());
+}
+
+RID RasterizerStorageRD::particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND_V(!particles_collision, RID());
+ ERR_FAIL_COND_V(particles_collision->type != RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE, RID());
+
+ if (particles_collision->heightfield_texture == RID()) {
+ //create
+ int resolutions[RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX] = { 256, 512, 1024, 2048, 4096, 8192 };
+ Size2i size;
+ if (particles_collision->extents.x > particles_collision->extents.z) {
+ size.x = resolutions[particles_collision->heightfield_resolution];
+ size.y = int32_t(particles_collision->extents.z / particles_collision->extents.x * size.x);
+ } else {
+ size.y = resolutions[particles_collision->heightfield_resolution];
+ size.x = int32_t(particles_collision->extents.x / particles_collision->extents.z * size.y);
+ }
+
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_D32_SFLOAT;
+ tf.width = size.x;
+ tf.height = size.y;
+ tf.type = RD::TEXTURE_TYPE_2D;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+
+ particles_collision->heightfield_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+ Vector<RID> fb_tex;
+ fb_tex.push_back(particles_collision->heightfield_texture);
+ particles_collision->heightfield_fb = RD::get_singleton()->framebuffer_create(fb_tex);
+ particles_collision->heightfield_fb_size = size;
+ }
+
+ return particles_collision->heightfield_fb;
+}
+
+void RasterizerStorageRD::particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ if (p_type == particles_collision->type) {
+ return;
+ }
+
+ if (particles_collision->heightfield_texture.is_valid()) {
+ RD::get_singleton()->free(particles_collision->heightfield_texture);
+ particles_collision->heightfield_texture = RID();
+ }
+ particles_collision->type = p_type;
+ particles_collision->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+ particles_collision->cull_mask = p_cull_mask;
+}
+
+void RasterizerStorageRD::particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->radius = p_radius;
+ particles_collision->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->extents = p_extents;
+ particles_collision->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->attractor_strength = p_strength;
+}
+
+void RasterizerStorageRD::particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->attractor_directionality = p_directionality;
+}
+
+void RasterizerStorageRD::particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->attractor_attenuation = p_curve;
+}
+
+void RasterizerStorageRD::particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ particles_collision->field_texture = p_texture;
+}
+
+void RasterizerStorageRD::particles_collision_height_field_update(RID p_particles_collision) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+ particles_collision->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND(!particles_collision);
+
+ if (particles_collision->heightfield_resolution == p_resolution) {
+ return;
+ }
+
+ particles_collision->heightfield_resolution = p_resolution;
+
+ if (particles_collision->heightfield_texture.is_valid()) {
+ RD::get_singleton()->free(particles_collision->heightfield_texture);
+ particles_collision->heightfield_texture = RID();
+ }
+}
+
+AABB RasterizerStorageRD::particles_collision_get_aabb(RID p_particles_collision) const {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND_V(!particles_collision, AABB());
+
+ switch (particles_collision->type) {
+ case RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT:
+ case RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE: {
+ AABB aabb;
+ aabb.position = -Vector3(1, 1, 1) * particles_collision->radius;
+ aabb.size = Vector3(2, 2, 2) * particles_collision->radius;
+ return aabb;
+ }
+ default: {
+ AABB aabb;
+ aabb.position = -particles_collision->extents;
+ aabb.size = particles_collision->extents * 2;
+ return aabb;
+ }
+ }
+
+ return AABB();
+}
+
+Vector3 RasterizerStorageRD::particles_collision_get_extents(RID p_particles_collision) const {
+ const ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND_V(!particles_collision, Vector3());
+ return particles_collision->extents;
+}
+
+bool RasterizerStorageRD::particles_collision_is_heightfield(RID p_particles_collision) const {
+ const ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_particles_collision);
+ ERR_FAIL_COND_V(!particles_collision, false);
+ return particles_collision->type == RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE;
+}
+
/* SKELETON API */
RID RasterizerStorageRD::skeleton_create() {
@@ -4680,6 +5063,9 @@ void RasterizerStorageRD::reflection_probe_set_extents(RID p_probe, const Vector
ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
ERR_FAIL_COND(!reflection_probe);
+ if (reflection_probe->extents == p_extents) {
+ return;
+ }
reflection_probe->extents = p_extents;
reflection_probe->instance_dependency.instance_notify_changed(true, false);
}
@@ -5797,6 +6183,9 @@ void RasterizerStorageRD::base_update_dependency(RID p_base, RasterizerScene::In
} else if (particles_owner.owns(p_base)) {
Particles *p = particles_owner.getornull(p_base);
p_instance->update_dependency(&p->instance_dependency);
+ } else if (particles_collision_owner.owns(p_base)) {
+ ParticlesCollision *pc = particles_collision_owner.getornull(p_base);
+ p_instance->update_dependency(&pc->instance_dependency);
}
}
@@ -5832,6 +6221,9 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
if (particles_owner.owns(p_rid)) {
return RS::INSTANCE_PARTICLES;
}
+ if (particles_collision_owner.owns(p_rid)) {
+ return RS::INSTANCE_PARTICLES_COLLISION;
+ }
return RS::INSTANCE_NONE;
}
@@ -6735,8 +7127,6 @@ void RasterizerStorageRD::update_dirty_resources() {
_update_dirty_multimeshes();
_update_dirty_skeletons();
_update_decal_atlas();
-
- update_particles();
}
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
@@ -6871,6 +7261,14 @@ bool RasterizerStorageRD::free(RID p_rid) {
_particles_free_data(particles);
particles->instance_dependency.instance_notify_deleted(p_rid);
particles_owner.free(p_rid);
+ } else if (particles_collision_owner.owns(p_rid)) {
+ ParticlesCollision *particles_collision = particles_collision_owner.getornull(p_rid);
+
+ if (particles_collision->heightfield_texture.is_valid()) {
+ RD::get_singleton()->free(particles_collision->heightfield_texture);
+ }
+ particles_collision->instance_dependency.instance_notify_deleted(p_rid);
+ particles_collision_owner.free(p_rid);
} else if (render_target_owner.owns(p_rid)) {
RenderTarget *rt = render_target_owner.getornull(p_rid);
@@ -7379,14 +7777,19 @@ RasterizerStorageRD::RasterizerStorageRD() {
actions.renames["RESTART_COLOR"] = "restart_color";
actions.renames["RESTART_CUSTOM"] = "restart_custom";
actions.renames["emit_particle"] = "emit_particle";
+ actions.renames["COLLIDED"] = "collided";
+ actions.renames["COLLISION_NORMAL"] = "collision_normal";
+ actions.renames["COLLISION_DEPTH"] = "collision_depth";
+ actions.renames["ATTRACTOR_FORCE"] = "attractor_force";
actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
+ actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISON_SCALE\n";
actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
- actions.texture_layout_set = 2;
+ actions.texture_layout_set = 3;
actions.base_uniform_string = "material.";
actions.base_varying_index = 10;
@@ -7481,7 +7884,12 @@ RasterizerStorageRD::~RasterizerStorageRD() {
for (int i = 0; i < DEFAULT_RD_BUFFER_MAX; i++) {
RD::get_singleton()->free(mesh_default_rd_buffers[i]);
}
+
giprobe_sdf_shader.version_free(giprobe_sdf_shader_version);
+ particles_shader.copy_shader.version_free(particles_shader.copy_shader_version);
+
+ RenderingServer::get_singleton()->free(particles_shader.default_material);
+ RenderingServer::get_singleton()->free(particles_shader.default_shader);
RD::get_singleton()->free(default_rd_storage_buffer);
diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
index e14b9528cf..b03a26e200 100644
--- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
+++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
@@ -475,6 +475,46 @@ private:
};
struct ParticlesFrameParams {
+ enum {
+ MAX_ATTRACTORS = 32,
+ MAX_COLLIDERS = 32,
+ MAX_3D_TEXTURES = 7
+ };
+
+ enum AttractorType {
+ ATTRACTOR_TYPE_SPHERE,
+ ATTRACTOR_TYPE_BOX,
+ ATTRACTOR_TYPE_VECTOR_FIELD,
+ };
+
+ struct Attractor {
+ float transform[16];
+ float extents[3]; //exents or radius
+ uint32_t type;
+
+ uint32_t texture_index; //texture index for vector field
+ float strength;
+ float attenuation;
+ float directionality;
+ };
+
+ enum CollisionType {
+ COLLISION_TYPE_SPHERE,
+ COLLISION_TYPE_BOX,
+ COLLISION_TYPE_SDF,
+ COLLISION_TYPE_HEIGHT_FIELD
+ };
+
+ struct Collider {
+ float transform[16];
+ float extents[3]; //exents or radius
+ uint32_t type;
+
+ uint32_t texture_index; //texture index for vector field
+ float scale;
+ uint32_t pad[2];
+ };
+
uint32_t emitting;
float system_phase;
float prev_system_phase;
@@ -486,9 +526,14 @@ private:
float delta;
uint32_t random_seed;
- uint32_t pad[3];
+ uint32_t attractor_count;
+ uint32_t collider_count;
+ float particle_size;
float emission_transform[16];
+
+ Attractor attractors[MAX_ATTRACTORS];
+ Collider colliders[MAX_COLLIDERS];
};
struct ParticleEmissionBufferData {
@@ -536,6 +581,11 @@ private:
RID particles_material_uniform_set;
RID particles_copy_uniform_set;
RID particles_transforms_buffer_uniform_set;
+ RID collision_textures_uniform_set;
+
+ RID collision_3d_textures[ParticlesFrameParams::MAX_3D_TEXTURES];
+ uint32_t collision_3d_textures_used = 0;
+ RID collision_heightmap_texture;
RID particles_sort_buffer;
RID particles_sort_uniform_set;
@@ -557,6 +607,7 @@ private:
int fixed_fps;
bool fractional_delta;
float frame_remainder;
+ float collision_base_size;
bool clear;
@@ -569,6 +620,8 @@ private:
ParticleEmissionBuffer *emission_buffer = nullptr;
RID emission_storage_buffer;
+ Set<RasterizerScene::InstanceBase *> collisions;
+
Particles() :
inactive(true),
inactive_time(0.0),
@@ -590,6 +643,7 @@ private:
fixed_fps(0),
fractional_delta(false),
frame_remainder(0),
+ collision_base_size(0.01),
clear(true) {
}
@@ -704,6 +758,28 @@ private:
mutable RID_Owner<Particles> particles_owner;
+ /* Particles Collision */
+
+ struct ParticlesCollision {
+ RS::ParticlesCollisionType type = RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT;
+ uint32_t cull_mask = 0xFFFFFFFF;
+ float radius = 1.0;
+ Vector3 extents = Vector3(1, 1, 1);
+ float attractor_strength = 1.0;
+ float attractor_attenuation = 1.0;
+ float attractor_directionality = 0.0;
+ RID field_texture;
+ RID heightfield_texture;
+ RID heightfield_fb;
+ Size2i heightfield_fb_size;
+
+ RS::ParticlesCollisionHeightfieldResolution heightfield_resolution = RS::PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024;
+
+ RasterizerScene::InstanceDependency instance_dependency;
+ };
+
+ mutable RID_Owner<ParticlesCollision> particles_collision_owner;
+
/* Skeleton */
struct Skeleton {
@@ -1691,6 +1767,7 @@ public:
void particles_set_process_material(RID p_particles, RID p_material);
void particles_set_fixed_fps(RID p_particles, int p_fps);
void particles_set_fractional_delta(RID p_particles, bool p_enable);
+ void particles_set_collision_base_size(RID p_particles, float p_size);
void particles_restart(RID p_particles);
void particles_emit(RID p_particles, const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags);
void particles_set_subemitter(RID p_particles, RID p_subemitter_particles);
@@ -1748,6 +1825,27 @@ public:
return particles->particles_transforms_buffer_uniform_set;
}
+ virtual void particles_add_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance);
+ virtual void particles_remove_collision(RID p_particles, RasterizerScene::InstanceBase *p_instance);
+
+ /* PARTICLES COLLISION */
+
+ virtual RID particles_collision_create();
+ virtual void particles_collision_set_collision_type(RID p_particles_collision, RS::ParticlesCollisionType p_type);
+ virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask);
+ virtual void particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius); //for spheres
+ virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents); //for non-spheres
+ virtual void particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength);
+ virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality);
+ virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve);
+ virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture); //for SDF and vector field, heightfield is dynamic
+ virtual void particles_collision_height_field_update(RID p_particles_collision); //for SDF and vector field
+ virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, RS::ParticlesCollisionHeightfieldResolution p_resolution); //for SDF and vector field
+ virtual AABB particles_collision_get_aabb(RID p_particles_collision) const;
+ virtual Vector3 particles_collision_get_extents(RID p_particles_collision) const;
+ virtual bool particles_collision_is_heightfield(RID p_particles_collision) const;
+ RID particles_collision_get_heightfield_framebuffer(RID p_particles_collision) const;
+
/* GLOBAL VARIABLES API */
virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value);
diff --git a/servers/rendering/rasterizer_rd/shaders/particles.glsl b/servers/rendering/rasterizer_rd/shaders/particles.glsl
index a924509771..926c7ef9fc 100644
--- a/servers/rendering/rasterizer_rd/shaders/particles.glsl
+++ b/servers/rendering/rasterizer_rd/shaders/particles.glsl
@@ -31,6 +31,40 @@ global_variables;
/* Set 1: FRAME AND PARTICLE DATA */
// a frame history is kept for trail deterministic behavior
+
+#define MAX_ATTRACTORS 32
+
+#define ATTRACTOR_TYPE_SPHERE 0
+#define ATTRACTOR_TYPE_BOX 1
+#define ATTRACTOR_TYPE_VECTOR_FIELD 2
+
+struct Attractor {
+ mat4 transform;
+ vec3 extents; //exents or radius
+ uint type;
+ uint texture_index; //texture index for vector field
+ float strength;
+ float attenuation;
+ float directionality;
+};
+
+#define MAX_COLLIDERS 32
+
+#define COLLIDER_TYPE_SPHERE 0
+#define COLLIDER_TYPE_BOX 1
+#define COLLIDER_TYPE_SDF 2
+#define COLLIDER_TYPE_HEIGHT_FIELD 3
+
+struct Collider {
+ mat4 transform;
+ vec3 extents; //exents or radius
+ uint type;
+
+ uint texture_index; //texture index for vector field
+ float scale;
+ uint pad[2];
+};
+
struct FrameParams {
bool emitting;
float system_phase;
@@ -43,9 +77,14 @@ struct FrameParams {
float delta;
uint random_seed;
- uint pad[3];
+ uint attractor_count;
+ uint collider_count;
+ float particle_size;
mat4 emission_transform;
+
+ Attractor attractors[MAX_ATTRACTORS];
+ Collider colliders[MAX_COLLIDERS];
};
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
@@ -80,7 +119,7 @@ struct ParticleEmission {
vec4 custom;
};
-layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmission {
+layout(set = 1, binding = 2, std430) restrict buffer SourceEmission {
int particle_count;
uint pad0;
uint pad1;
@@ -89,7 +128,7 @@ layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmi
}
src_particles;
-layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmission {
+layout(set = 1, binding = 3, std430) restrict buffer DestEmission {
int particle_count;
int particle_max;
uint pad1;
@@ -98,10 +137,17 @@ layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmiss
}
dst_particles;
-/* SET 2: MATERIAL */
+/* SET 2: COLLIDER/ATTRACTOR TEXTURES */
+
+#define MAX_3D_TEXTURES 7
+
+layout(set = 2, binding = 0) uniform texture3D sdf_vec_textures[MAX_3D_TEXTURES];
+layout(set = 2, binding = 1) uniform texture2D height_field_texture;
+
+/* SET 3: MATERIAL */
#ifdef USE_MATERIAL_UNIFORMS
-layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
+layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
/* clang-format off */
MATERIAL_UNIFORMS
/* clang-format on */
@@ -140,29 +186,7 @@ bool emit_particle(mat4 p_xform, vec3 p_velocity, vec4 p_color, vec4 p_custom, u
atomicAdd(dst_particles.particle_count, -1);
return false;
}
- /*
- valid = true;
-
- int attempts = 256; // never trust compute
- while(attempts-- > 0) {
- dst_index = dst_particles.particle_count;
- if (dst_index == dst_particles.particle_max) {
- return false; //can't emit anymore
- }
-
- if (atomicCompSwap(dst_particles.particle_count, dst_index, dst_index +1 ) != dst_index) {
- continue;
- }
- valid=true;
- break;
- }
- barrier();
-
- if (!valid) {
- return false; //gave up (attempts exhausted)
- }
-*/
dst_particles.data[dst_index].xform = p_xform;
dst_particles.data[dst_index].velocity = p_velocity;
dst_particles.data[dst_index].color = p_color;
@@ -217,6 +241,199 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
}
+ bool collided = false;
+ vec3 collision_normal = vec3(0.0);
+ float collision_depth = 0.0;
+
+ vec3 attractor_force = vec3(0.0);
+
+#if !defined(DISABLE_VELOCITY)
+
+ if (PARTICLE.is_active) {
+ PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
+ }
+#endif
+
+ /* Process physics if active */
+
+ if (PARTICLE.is_active) {
+ for (uint i = 0; i < FRAME.attractor_count; i++) {
+ vec3 dir;
+ float amount;
+ vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.attractors[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(FRAME.attractors[i].transform);
+
+ switch (FRAME.attractors[i].type) {
+ case ATTRACTOR_TYPE_SPHERE: {
+ dir = normalize(rel_vec);
+ float d = length(local_pos) / FRAME.attractors[i].extents.x;
+ if (d > 1.0) {
+ continue;
+ }
+ amount = max(0.0, 1.0 - d);
+ } break;
+ case ATTRACTOR_TYPE_BOX: {
+ dir = normalize(rel_vec);
+
+ vec3 abs_pos = abs(local_pos / FRAME.attractors[i].extents);
+ float d = max(abs_pos.x, max(abs_pos.y, abs_pos.z));
+ if (d > 1.0) {
+ continue;
+ }
+ amount = max(0.0, 1.0 - d);
+
+ } break;
+ case ATTRACTOR_TYPE_VECTOR_FIELD: {
+ vec3 uvw_pos = (local_pos / FRAME.attractors[i].extents) * 2.0 - 1.0;
+ if (any(lessThan(uvw_pos, vec3(0.0))) || any(greaterThan(uvw_pos, vec3(1.0)))) {
+ continue;
+ }
+ vec3 s = texture(sampler3D(sdf_vec_textures[FRAME.attractors[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).xyz;
+ dir = mat3(FRAME.attractors[i].transform) * normalize(s); //revert direction
+ amount = length(s);
+
+ } break;
+ }
+ amount = pow(amount, FRAME.attractors[i].attenuation);
+ dir = normalize(mix(dir, FRAME.attractors[i].transform[2].xyz, FRAME.attractors[i].directionality));
+ attractor_force -= amount * dir * FRAME.attractors[i].strength;
+ }
+
+ float particle_size = FRAME.particle_size;
+
+#ifdef USE_COLLISON_SCALE
+
+ particle_size *= dot(vec3(length(PARTICLE.xform[0].xyz), length(PARTICLE.xform[1].xyz), length(PARTICLE.xform[2].xyz)), vec3(0.33333333333));
+
+#endif
+
+ for (uint i = 0; i < FRAME.collider_count; i++) {
+ vec3 normal;
+ float depth;
+ bool col = false;
+
+ vec3 rel_vec = PARTICLE.xform[3].xyz - FRAME.colliders[i].transform[3].xyz;
+ vec3 local_pos = rel_vec * mat3(FRAME.colliders[i].transform);
+
+ switch (FRAME.colliders[i].type) {
+ case COLLIDER_TYPE_SPHERE: {
+ float d = length(rel_vec) - (particle_size + FRAME.colliders[i].extents.x);
+
+ if (d < 0.0) {
+ col = true;
+ depth = -d;
+ normal = normalize(rel_vec);
+ }
+
+ } break;
+ case COLLIDER_TYPE_BOX: {
+ vec3 abs_pos = abs(local_pos);
+ vec3 sgn_pos = sign(local_pos);
+
+ if (any(greaterThan(abs_pos, FRAME.colliders[i].extents))) {
+ //point outside box
+
+ vec3 closest = min(abs_pos, FRAME.colliders[i].extents);
+ vec3 rel = abs_pos - closest;
+ depth = length(rel) - particle_size;
+ if (depth < 0.0) {
+ col = true;
+ normal = mat3(FRAME.colliders[i].transform) * (normalize(rel) * sgn_pos);
+ depth = -depth;
+ }
+ } else {
+ //point inside box
+ vec3 axis_len = FRAME.colliders[i].extents - abs_pos;
+ // there has to be a faster way to do this?
+ if (all(lessThan(axis_len.xx, axis_len.yz))) {
+ normal = vec3(1, 0, 0);
+ } else if (all(lessThan(axis_len.yy, axis_len.xz))) {
+ normal = vec3(0, 1, 0);
+ } else {
+ normal = vec3(0, 0, 1);
+ }
+
+ col = true;
+ depth = dot(normal * axis_len, vec3(1)) + particle_size;
+ normal = mat3(FRAME.colliders[i].transform) * (normal * sgn_pos);
+ }
+
+ } break;
+ case COLLIDER_TYPE_SDF: {
+ vec3 apos = abs(local_pos);
+ float extra_dist = 0.0;
+ if (any(greaterThan(apos, FRAME.colliders[i].extents))) { //outside
+ vec3 mpos = min(apos, FRAME.colliders[i].extents);
+ extra_dist = distance(mpos, apos);
+ }
+
+ if (extra_dist > particle_size) {
+ continue;
+ }
+
+ vec3 uvw_pos = (local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5;
+ float s = texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos).r;
+ s *= FRAME.colliders[i].scale;
+ s += extra_dist;
+ if (s < particle_size) {
+ col = true;
+ depth = particle_size - s;
+ const float EPSILON = 0.001;
+ normal = mat3(FRAME.colliders[i].transform) *
+ normalize(
+ vec3(
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(EPSILON, 0.0, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, EPSILON, 0.0)).r,
+ texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_vec_textures[FRAME.colliders[i].texture_index], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos - vec3(0.0, 0.0, EPSILON)).r));
+ }
+
+ } break;
+ case COLLIDER_TYPE_HEIGHT_FIELD: {
+ vec3 local_pos_bottom = local_pos;
+ local_pos_bottom.y -= particle_size;
+
+ if (any(greaterThan(abs(local_pos_bottom), FRAME.colliders[i].extents))) {
+ continue;
+ }
+
+ const float DELTA = 1.0 / 8192.0;
+
+ vec3 uvw_pos = vec3(local_pos_bottom / FRAME.colliders[i].extents) * 0.5 + 0.5;
+
+ float y = 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz).r;
+
+ if (y > uvw_pos.y) {
+ //inside heightfield
+
+ vec3 pos1 = (vec3(uvw_pos.x, y, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos2 = (vec3(uvw_pos.x + DELTA, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(DELTA, 0)).r, uvw_pos.z) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+ vec3 pos3 = (vec3(uvw_pos.x, 1.0 - texture(sampler2D(height_field_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), uvw_pos.xz + vec2(0, DELTA)).r, uvw_pos.z + DELTA) * 2.0 - 1.0) * FRAME.colliders[i].extents;
+
+ normal = normalize(cross(pos1 - pos2, pos1 - pos3));
+ float local_y = (vec3(local_pos / FRAME.colliders[i].extents) * 0.5 + 0.5).y;
+
+ col = true;
+ depth = dot(normal, pos1) - dot(normal, local_pos_bottom);
+ }
+
+ } break;
+ }
+
+ if (col) {
+ if (!collided) {
+ collided = true;
+ collision_normal = normal;
+ collision_depth = depth;
+ } else {
+ vec3 c = collision_normal * collision_depth;
+ c += normal * max(0.0, depth - dot(normal, c));
+ collision_normal = normalize(c);
+ collision_depth = length(c);
+ }
+ }
+ }
+ }
+
if (params.sub_emitter_mode) {
if (!PARTICLE.is_active) {
int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
@@ -329,66 +546,4 @@ COMPUTE_SHADER_CODE
/* clang-format on */
}
-
-#if !defined(DISABLE_VELOCITY)
-
- if (PARTICLE.is_active) {
- PARTICLE.xform[3].xyz += PARTICLE.velocity * local_delta;
- }
-#endif
-
-#if 0
- if (PARTICLE.is_active) {
- //execute shader
-
-
-
-
- //!defined(DISABLE_FORCE)
-
- if (false) {
- vec3 force = vec3(0.0);
- for (int i = 0; i < attractor_count; i++) {
- vec3 rel_vec = xform[3].xyz - attractors[i].pos;
- float dist = length(rel_vec);
- if (attractors[i].radius < dist)
- continue;
- if (attractors[i].eat_radius > 0.0 && attractors[i].eat_radius > dist) {
- out_velocity_active.a = 0.0;
- }
-
- rel_vec = normalize(rel_vec);
-
- float attenuation = pow(dist / attractors[i].radius, attractors[i].attenuation);
-
- if (attractors[i].dir == vec3(0.0)) {
- //towards center
- force += attractors[i].strength * rel_vec * attenuation * mass;
- } else {
- force += attractors[i].strength * attractors[i].dir * attenuation * mass;
- }
- }
-
- out_velocity_active.xyz += force * local_delta;
- }
-
-#if !defined(DISABLE_VELOCITY)
-
- if (true) {
- xform[3].xyz += out_velocity_active.xyz * local_delta;
- }
-#endif
- } else {
- xform = mat4(0.0);
- }
-
-
- xform = transpose(xform);
-
- out_velocity_active.a = mix(0.0, 1.0, shader_active);
-
- out_xform_1 = xform[0];
- out_xform_2 = xform[1];
- out_xform_3 = xform[2];
-#endif
}
diff --git a/servers/rendering/rendering_server_raster.cpp b/servers/rendering/rendering_server_raster.cpp
index b12e2ff3c1..cbc91497ba 100644
--- a/servers/rendering/rendering_server_raster.cpp
+++ b/servers/rendering/rendering_server_raster.cpp
@@ -108,6 +108,9 @@ void RenderingServerRaster::draw(bool p_swap_buffers, double frame_step) {
RSG::scene->update_dirty_instances(); //update scene stuff
+ RSG::scene->render_particle_colliders();
+ RSG::storage->update_particles(); //need to be done after instances are updated (colliders and particle transforms), and colliders are rendered
+
RSG::scene->render_probes();
RSG::viewport->draw_viewports();
RSG::canvas_render->update();
diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h
index b554425bef..afb3d6f46f 100644
--- a/servers/rendering/rendering_server_raster.h
+++ b/servers/rendering/rendering_server_raster.h
@@ -452,6 +452,7 @@ public:
BIND1(particles_restart, RID)
BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
BIND2(particles_set_subemitter, RID, RID)
+ BIND2(particles_set_collision_base_size, RID, float)
BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
@@ -461,6 +462,21 @@ public:
BIND1R(AABB, particles_get_current_aabb, RID)
BIND2(particles_set_emission_transform, RID, const Transform &)
+ /* PARTICLES COLLISION */
+
+ BIND0R(RID, particles_collision_create)
+
+ BIND2(particles_collision_set_collision_type, RID, ParticlesCollisionType)
+ BIND2(particles_collision_set_cull_mask, RID, uint32_t)
+ BIND2(particles_collision_set_sphere_radius, RID, float)
+ BIND2(particles_collision_set_box_extents, RID, const Vector3 &)
+ BIND2(particles_collision_set_attractor_strength, RID, float)
+ BIND2(particles_collision_set_attractor_directionality, RID, float)
+ BIND2(particles_collision_set_attractor_attenuation, RID, float)
+ BIND2(particles_collision_set_field_texture, RID, RID)
+ BIND1(particles_collision_height_field_update, RID)
+ BIND2(particles_collision_set_height_field_resolution, RID, ParticlesCollisionHeightfieldResolution)
+
#undef BINDBASE
//from now on, calls forwarded to this singleton
#define BINDBASE RSG::scene
diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp
index d8e52a5aae..ae6786090a 100644
--- a/servers/rendering/rendering_server_scene.cpp
+++ b/servers/rendering/rendering_server_scene.cpp
@@ -193,6 +193,8 @@ void *RenderingServerScene::_instance_pair(void *p_self, OctreeElementID, Instan
} else if (B->base_type == RS::INSTANCE_GI_PROBE && A->base_type == RS::INSTANCE_LIGHT) {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
return gi_probe->lights.insert(A);
+ } else if (B->base_type == RS::INSTANCE_PARTICLES_COLLISION && A->base_type == RS::INSTANCE_PARTICLES) {
+ RSG::storage->particles_add_collision(A->base, B);
}
return nullptr;
@@ -274,6 +276,8 @@ void RenderingServerScene::_instance_unpair(void *p_self, OctreeElementID, Insta
Set<Instance *>::Element *E = reinterpret_cast<Set<Instance *>::Element *>(udata);
gi_probe->lights.erase(E);
+ } else if (B->base_type == RS::INSTANCE_PARTICLES_COLLISION && A->base_type == RS::INSTANCE_PARTICLES) {
+ RSG::storage->particles_remove_collision(A->base, B);
}
}
@@ -539,6 +543,9 @@ void RenderingServerScene::instance_set_scenario(RID p_instance, RID p_scenario)
RSG::scene_render->reflection_probe_release_atlas_index(reflection_probe->instance);
} break;
+ case RS::INSTANCE_PARTICLES_COLLISION: {
+ heightfield_particle_colliders_update_list.erase(instance);
+ } break;
case RS::INSTANCE_GI_PROBE: {
InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
@@ -702,6 +709,12 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible)
}
} break;
+ case RS::INSTANCE_PARTICLES_COLLISION: {
+ if (instance->octree_id && instance->scenario) {
+ instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_PARTICLES_COLLISION, p_visible ? (1 << RS::INSTANCE_PARTICLES) : 0);
+ }
+
+ } break;
default: {
}
}
@@ -1026,6 +1039,13 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
RSG::storage->particles_set_emission_transform(p_instance->base, p_instance->transform);
}
+ if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) {
+ //remove materials no longer used and un-own them
+ if (RSG::storage->particles_collision_is_heightfield(p_instance->base)) {
+ heightfield_particle_colliders_update_list.insert(p_instance);
+ }
+ }
+
if (p_instance->aabb.has_no_surface()) {
return;
}
@@ -1085,6 +1105,11 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
pairable = true;
}
+ if (p_instance->base_type == RS::INSTANCE_PARTICLES_COLLISION) {
+ pairable_mask = p_instance->visible ? (1 << RS::INSTANCE_PARTICLES) : 0;
+ pairable = true;
+ }
+
if (p_instance->base_type == RS::INSTANCE_GI_PROBE) {
//lights and geometries
pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT) : 0;
@@ -1146,6 +1171,10 @@ void RenderingServerScene::_update_instance_aabb(Instance *p_instance) {
}
} break;
+ case RenderingServer::INSTANCE_PARTICLES_COLLISION: {
+ new_aabb = RSG::storage->particles_collision_get_aabb(p_instance->base);
+
+ } break;
case RenderingServer::INSTANCE_LIGHT: {
new_aabb = RSG::storage->light_get_aabb(p_instance->base);
@@ -2679,6 +2708,27 @@ void RenderingServerScene::render_probes() {
}
}
+void RenderingServerScene::render_particle_colliders() {
+ while (heightfield_particle_colliders_update_list.front()) {
+ Instance *hfpc = heightfield_particle_colliders_update_list.front()->get();
+
+ if (hfpc->scenario && hfpc->base_type == RS::INSTANCE_PARTICLES_COLLISION && RSG::storage->particles_collision_is_heightfield(hfpc->base)) {
+ //update heightfield
+ int cull_count = hfpc->scenario->octree.cull_aabb(hfpc->transformed_aabb, instance_cull_result, MAX_INSTANCE_CULL); //@TODO: cull mask missing
+ for (int i = 0; i < cull_count; i++) {
+ Instance *instance = instance_cull_result[i];
+ if (!instance->visible || !((1 << instance->base_type) & (RS::INSTANCE_GEOMETRY_MASK & (~(1 << RS::INSTANCE_PARTICLES))))) { //all but particles to avoid self collision
+ cull_count--;
+ SWAP(instance_cull_result[i], instance_cull_result[cull_count]);
+ }
+ }
+
+ RSG::scene_render->render_particle_collider_heightfield(hfpc->base, hfpc->transform, (RasterizerScene::InstanceBase **)instance_cull_result, cull_count);
+ }
+ heightfield_particle_colliders_update_list.erase(heightfield_particle_colliders_update_list.front());
+ }
+}
+
void RenderingServerScene::_update_instance_shader_parameters_from_material(Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter> &isparams, const Map<StringName, RasterizerScene::InstanceBase::InstanceShaderParameter> &existing_isparams, RID p_material) {
List<RasterizerStorage::InstanceShaderParam> plist;
RSG::storage->material_get_instance_shader_parameters(p_material, &plist);
diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h
index 165c3784c1..1b0a617627 100644
--- a/servers/rendering/rendering_server_scene.h
+++ b/servers/rendering/rendering_server_scene.h
@@ -385,6 +385,8 @@ public:
}
};
+ Set<Instance *> heightfield_particle_colliders_update_list;
+
int instance_cull_count;
Instance *instance_cull_result[MAX_INSTANCE_CULL];
Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
@@ -461,6 +463,7 @@ public:
void render_camera(RID p_render_buffers, Ref<XRInterface> &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas);
void update_dirty_instances();
+ void render_particle_colliders();
void render_probes();
TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size);
diff --git a/servers/rendering/rendering_server_wrap_mt.cpp b/servers/rendering/rendering_server_wrap_mt.cpp
index 9aa6593cbe..ab9856e06e 100644
--- a/servers/rendering/rendering_server_wrap_mt.cpp
+++ b/servers/rendering/rendering_server_wrap_mt.cpp
@@ -124,6 +124,7 @@ void RenderingServerWrapMT::finish() {
gi_probe_free_cached_ids();
lightmap_free_cached_ids();
particles_free_cached_ids();
+ particles_collision_free_cached_ids();
camera_free_cached_ids();
viewport_free_cached_ids();
environment_free_cached_ids();
diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h
index 372a7269dc..910acd74cb 100644
--- a/servers/rendering/rendering_server_wrap_mt.h
+++ b/servers/rendering/rendering_server_wrap_mt.h
@@ -356,6 +356,8 @@ public:
FUNC2(particles_set_process_material, RID, RID)
FUNC2(particles_set_fixed_fps, RID, int)
FUNC2(particles_set_fractional_delta, RID, bool)
+ FUNC2(particles_set_collision_base_size, RID, float)
+
FUNC1R(bool, particles_is_inactive, RID)
FUNC1(particles_request_process, RID)
FUNC1(particles_restart, RID)
@@ -371,6 +373,21 @@ public:
FUNC1R(AABB, particles_get_current_aabb, RID)
+ /* PARTICLES COLLISION */
+
+ FUNCRID(particles_collision)
+
+ FUNC2(particles_collision_set_collision_type, RID, ParticlesCollisionType)
+ FUNC2(particles_collision_set_cull_mask, RID, uint32_t)
+ FUNC2(particles_collision_set_sphere_radius, RID, float)
+ FUNC2(particles_collision_set_box_extents, RID, const Vector3 &)
+ FUNC2(particles_collision_set_attractor_strength, RID, float)
+ FUNC2(particles_collision_set_attractor_directionality, RID, float)
+ FUNC2(particles_collision_set_attractor_attenuation, RID, float)
+ FUNC2(particles_collision_set_field_texture, RID, RID)
+ FUNC1(particles_collision_height_field_update, RID)
+ FUNC2(particles_collision_set_height_field_resolution, RID, ParticlesCollisionHeightfieldResolution)
+
/* CAMERA API */
FUNCRID(camera)
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 6d473a75f2..f1209d9d6d 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -295,6 +295,10 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_COLOR"] = constt(ShaderLanguage::TYPE_BOOL);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["RESTART_CUSTOM"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLIDED"] = constt(ShaderLanguage::TYPE_BOOL);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLISION_NORMAL"] = constt(ShaderLanguage::TYPE_VEC3);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["COLLISION_DEPTH"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["compute"].built_ins["ATTRACTOR_FORCE"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_PARTICLES].functions["compute"].can_discard = false;
{
@@ -308,6 +312,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_PARTICLES].functions["compute"].stage_functions["emit_particle"] = emit_vertex_func;
}
+ shader_modes[RS::SHADER_PARTICLES].modes.push_back("collision_use_scale");
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_force");
shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data");
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 9ad19e4b38..f4b3634d99 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2152,6 +2152,7 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(INSTANCE_MULTIMESH);
BIND_ENUM_CONSTANT(INSTANCE_IMMEDIATE);
BIND_ENUM_CONSTANT(INSTANCE_PARTICLES);
+ BIND_ENUM_CONSTANT(INSTANCE_PARTICLES_COLLISION);
BIND_ENUM_CONSTANT(INSTANCE_LIGHT);
BIND_ENUM_CONSTANT(INSTANCE_REFLECTION_PROBE);
BIND_ENUM_CONSTANT(INSTANCE_DECAL);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 7dae45f126..7680dc1390 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -578,6 +578,7 @@ public:
virtual void particles_set_process_material(RID p_particles, RID p_material) = 0;
virtual void particles_set_fixed_fps(RID p_particles, int p_fps) = 0;
virtual void particles_set_fractional_delta(RID p_particles, bool p_enable) = 0;
+ virtual void particles_set_collision_base_size(RID p_particles, float p_size) = 0;
virtual bool particles_is_inactive(RID p_particles) = 0;
virtual void particles_request_process(RID p_particles) = 0;
virtual void particles_restart(RID p_particles) = 0;
@@ -609,6 +610,43 @@ public:
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
+ /* PARTICLES COLLISION API */
+
+ virtual RID particles_collision_create() = 0;
+
+ enum ParticlesCollisionType {
+ PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT,
+ PARTICLES_COLLISION_TYPE_BOX_ATTRACT,
+ PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT,
+ PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE,
+ PARTICLES_COLLISION_TYPE_BOX_COLLIDE,
+ PARTICLES_COLLISION_TYPE_SDF_COLLIDE,
+ PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE,
+ };
+
+ virtual void particles_collision_set_collision_type(RID p_particles_collision, ParticlesCollisionType p_type) = 0;
+ virtual void particles_collision_set_cull_mask(RID p_particles_collision, uint32_t p_cull_mask) = 0;
+ virtual void particles_collision_set_sphere_radius(RID p_particles_collision, float p_radius) = 0; //for spheres
+ virtual void particles_collision_set_box_extents(RID p_particles_collision, const Vector3 &p_extents) = 0; //for non-spheres
+ virtual void particles_collision_set_attractor_strength(RID p_particles_collision, float p_strength) = 0;
+ virtual void particles_collision_set_attractor_directionality(RID p_particles_collision, float p_directionality) = 0;
+ virtual void particles_collision_set_attractor_attenuation(RID p_particles_collision, float p_curve) = 0;
+ virtual void particles_collision_set_field_texture(RID p_particles_collision, RID p_texture) = 0; //for SDF and vector field, heightfield is dynamic
+
+ virtual void particles_collision_height_field_update(RID p_particles_collision) = 0; //for SDF and vector field
+
+ enum ParticlesCollisionHeightfieldResolution { //longest axis resolution
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_256,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_512,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_1024,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_2048,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_4096,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_8192,
+ PARTICLES_COLLISION_HEIGHTFIELD_RESOLUTION_MAX,
+ };
+
+ virtual void particles_collision_set_height_field_resolution(RID p_particles_collision, ParticlesCollisionHeightfieldResolution p_resolution) = 0; //for SDF and vector field
+
/* CAMERA API */
virtual RID camera_create() = 0;
@@ -965,6 +1003,7 @@ public:
INSTANCE_MULTIMESH,
INSTANCE_IMMEDIATE,
INSTANCE_PARTICLES,
+ INSTANCE_PARTICLES_COLLISION,
INSTANCE_LIGHT,
INSTANCE_REFLECTION_PROBE,
INSTANCE_DECAL,