mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-03 23:21:15 +00:00 
			
		
		
		
	Implement GPU Particle Collisions
-Sphere Attractor -Box Attractor -Vector Field -Sphere Collider -Box Collider -Baked SDF Collider -Heightmap Collider
This commit is contained in:
		
							parent
							
								
									c35005ba25
								
							
						
					
					
						commit
						26f5bd245c
					
				
					 32 changed files with 2947 additions and 102 deletions
				
			
		| 
						 | 
					@ -65,6 +65,14 @@ struct Vector2 {
 | 
				
			||||||
	real_t length() const;
 | 
						real_t length() const;
 | 
				
			||||||
	real_t length_squared() 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_to(const Vector2 &p_vector2) const;
 | 
				
			||||||
	real_t distance_squared_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;
 | 
						real_t angle_to(const Vector2 &p_vector2) const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,13 +73,15 @@ class ThreadWorkPool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ThreadData *threads = nullptr;
 | 
						ThreadData *threads = nullptr;
 | 
				
			||||||
	uint32_t thread_count = 0;
 | 
						uint32_t thread_count = 0;
 | 
				
			||||||
 | 
						BaseWork *current_work = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static void _thread_function(ThreadData *p_thread);
 | 
						static void _thread_function(ThreadData *p_thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	template <class C, class M, class U>
 | 
						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(!threads); //never initialized
 | 
				
			||||||
 | 
							ERR_FAIL_COND(current_work != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		index.store(0);
 | 
							index.store(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -90,16 +92,37 @@ public:
 | 
				
			||||||
		w->index = &index;
 | 
							w->index = &index;
 | 
				
			||||||
		w->max_elements = p_elements;
 | 
							w->max_elements = p_elements;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							current_work = w;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (uint32_t i = 0; i < thread_count; i++) {
 | 
							for (uint32_t i = 0; i < thread_count; i++) {
 | 
				
			||||||
			threads[i].work = w;
 | 
								threads[i].work = w;
 | 
				
			||||||
			threads[i].start.post();
 | 
								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++) {
 | 
							for (uint32_t i = 0; i < thread_count; i++) {
 | 
				
			||||||
			threads[i].completed.wait();
 | 
								threads[i].completed.wait();
 | 
				
			||||||
			threads[i].work = nullptr;
 | 
								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);
 | 
						void init(int p_thread_count = -1);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +128,7 @@
 | 
				
			||||||
#include "editor/plugins/gi_probe_editor_plugin.h"
 | 
					#include "editor/plugins/gi_probe_editor_plugin.h"
 | 
				
			||||||
#include "editor/plugins/gpu_particles_2d_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_3d_editor_plugin.h"
 | 
				
			||||||
 | 
					#include "editor/plugins/gpu_particles_collision_sdf_editor_plugin.h"
 | 
				
			||||||
#include "editor/plugins/gradient_editor_plugin.h"
 | 
					#include "editor/plugins/gradient_editor_plugin.h"
 | 
				
			||||||
#include "editor/plugins/item_list_editor_plugin.h"
 | 
					#include "editor/plugins/item_list_editor_plugin.h"
 | 
				
			||||||
#include "editor/plugins/light_occluder_2d_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(PhysicalBone3DEditorPlugin(this)));
 | 
				
			||||||
	add_editor_plugin(memnew(MeshEditorPlugin(this)));
 | 
						add_editor_plugin(memnew(MeshEditorPlugin(this)));
 | 
				
			||||||
	add_editor_plugin(memnew(MaterialEditorPlugin(this)));
 | 
						add_editor_plugin(memnew(MaterialEditorPlugin(this)));
 | 
				
			||||||
 | 
						add_editor_plugin(memnew(GPUParticlesCollisionSDFEditorPlugin(this)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
 | 
						for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
 | 
				
			||||||
		add_editor_plugin(EditorPlugins::create(i, this));
 | 
							add_editor_plugin(EditorPlugins::create(i, this));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ String ResourceImporterLayeredTexture::get_importer_name() const {
 | 
				
			||||||
			return "cubemap_array_texture";
 | 
								return "cubemap_array_texture";
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
		case MODE_3D: {
 | 
							case MODE_3D: {
 | 
				
			||||||
			return "cubemap_3d_texture";
 | 
								return "3d_texture";
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,7 @@
 | 
				
			||||||
#include "scene/3d/decal.h"
 | 
					#include "scene/3d/decal.h"
 | 
				
			||||||
#include "scene/3d/gi_probe.h"
 | 
					#include "scene/3d/gi_probe.h"
 | 
				
			||||||
#include "scene/3d/gpu_particles_3d.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/light_3d.h"
 | 
				
			||||||
#include "scene/3d/lightmap_probe.h"
 | 
					#include "scene/3d/lightmap_probe.h"
 | 
				
			||||||
#include "scene/3d/listener_3d.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() {
 | 
					ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() {
 | 
				
			||||||
	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
 | 
						Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -253,6 +253,23 @@ public:
 | 
				
			||||||
	GPUParticles3DGizmoPlugin();
 | 
						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 {
 | 
					class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
 | 
				
			||||||
	GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin);
 | 
						GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										201
									
								
								editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										74
									
								
								editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -6092,6 +6092,7 @@ void Node3DEditor::_register_all_gizmos() {
 | 
				
			||||||
	add_gizmo_plugin(Ref<VehicleWheel3DGizmoPlugin>(memnew(VehicleWheel3DGizmoPlugin)));
 | 
						add_gizmo_plugin(Ref<VehicleWheel3DGizmoPlugin>(memnew(VehicleWheel3DGizmoPlugin)));
 | 
				
			||||||
	add_gizmo_plugin(Ref<VisibilityNotifier3DGizmoPlugin>(memnew(VisibilityNotifier3DGizmoPlugin)));
 | 
						add_gizmo_plugin(Ref<VisibilityNotifier3DGizmoPlugin>(memnew(VisibilityNotifier3DGizmoPlugin)));
 | 
				
			||||||
	add_gizmo_plugin(Ref<GPUParticles3DGizmoPlugin>(memnew(GPUParticles3DGizmoPlugin)));
 | 
						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<CPUParticles3DGizmoPlugin>(memnew(CPUParticles3DGizmoPlugin)));
 | 
				
			||||||
	add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 | 
						add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
 | 
				
			||||||
	add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
 | 
						add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,6 +124,11 @@ void GPUParticles3D::set_speed_scale(float p_scale) {
 | 
				
			||||||
	RS::get_singleton()->particles_set_speed_scale(particles, 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 {
 | 
					bool GPUParticles3D::is_emitting() const {
 | 
				
			||||||
	return RS::get_singleton()->particles_get_emitting(particles);
 | 
						return RS::get_singleton()->particles_get_emitting(particles);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -168,6 +173,10 @@ float GPUParticles3D::get_speed_scale() const {
 | 
				
			||||||
	return speed_scale;
 | 
						return speed_scale;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float GPUParticles3D::get_collision_base_size() const {
 | 
				
			||||||
 | 
						return collision_base_size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GPUParticles3D::set_draw_order(DrawOrder p_order) {
 | 
					void GPUParticles3D::set_draw_order(DrawOrder p_order) {
 | 
				
			||||||
	draw_order = p_order;
 | 
						draw_order = p_order;
 | 
				
			||||||
	RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(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_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_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_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("is_emitting"), &GPUParticles3D::is_emitting);
 | 
				
			||||||
	ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles3D::get_amount);
 | 
						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_fractional_delta"), &GPUParticles3D::get_fractional_delta);
 | 
				
			||||||
	ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material);
 | 
						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_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);
 | 
						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::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::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps");
 | 
				
			||||||
	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
 | 
						ADD_PROPERTY(PropertyInfo(Variant::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_GROUP("Drawing", "");
 | 
				
			||||||
	ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb");
 | 
						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");
 | 
						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_passes(1);
 | 
				
			||||||
	set_draw_order(DRAW_ORDER_INDEX);
 | 
						set_draw_order(DRAW_ORDER_INDEX);
 | 
				
			||||||
	set_speed_scale(1);
 | 
						set_speed_scale(1);
 | 
				
			||||||
 | 
						set_collision_base_size(0.01);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GPUParticles3D::~GPUParticles3D() {
 | 
					GPUParticles3D::~GPUParticles3D() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,6 +65,7 @@ private:
 | 
				
			||||||
	int fixed_fps;
 | 
						int fixed_fps;
 | 
				
			||||||
	bool fractional_delta;
 | 
						bool fractional_delta;
 | 
				
			||||||
	NodePath sub_emitter;
 | 
						NodePath sub_emitter;
 | 
				
			||||||
 | 
						float collision_base_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Ref<Material> process_material;
 | 
						Ref<Material> process_material;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +95,7 @@ public:
 | 
				
			||||||
	void set_use_local_coordinates(bool p_enable);
 | 
						void set_use_local_coordinates(bool p_enable);
 | 
				
			||||||
	void set_process_material(const Ref<Material> &p_material);
 | 
						void set_process_material(const Ref<Material> &p_material);
 | 
				
			||||||
	void set_speed_scale(float p_scale);
 | 
						void set_speed_scale(float p_scale);
 | 
				
			||||||
 | 
						void set_collision_base_size(float p_ratio);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool is_emitting() const;
 | 
						bool is_emitting() const;
 | 
				
			||||||
	int get_amount() const;
 | 
						int get_amount() const;
 | 
				
			||||||
| 
						 | 
					@ -106,6 +108,7 @@ public:
 | 
				
			||||||
	bool get_use_local_coordinates() const;
 | 
						bool get_use_local_coordinates() const;
 | 
				
			||||||
	Ref<Material> get_process_material() const;
 | 
						Ref<Material> get_process_material() const;
 | 
				
			||||||
	float get_speed_scale() const;
 | 
						float get_speed_scale() const;
 | 
				
			||||||
 | 
						float get_collision_base_size() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void set_fixed_fps(int p_count);
 | 
						void set_fixed_fps(int p_count);
 | 
				
			||||||
	int get_fixed_fps() const;
 | 
						int get_fixed_fps() const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										897
									
								
								scene/3d/gpu_particles_collision_3d.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										897
									
								
								scene/3d/gpu_particles_collision_3d.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,897 @@
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/*  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(¶ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										342
									
								
								scene/3d/gpu_particles_collision_3d.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										342
									
								
								scene/3d/gpu_particles_collision_3d.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -194,6 +194,7 @@
 | 
				
			||||||
#include "scene/3d/decal.h"
 | 
					#include "scene/3d/decal.h"
 | 
				
			||||||
#include "scene/3d/gi_probe.h"
 | 
					#include "scene/3d/gi_probe.h"
 | 
				
			||||||
#include "scene/3d/gpu_particles_3d.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/immediate_geometry_3d.h"
 | 
				
			||||||
#include "scene/3d/light_3d.h"
 | 
					#include "scene/3d/light_3d.h"
 | 
				
			||||||
#include "scene/3d/lightmap_probe.h"
 | 
					#include "scene/3d/lightmap_probe.h"
 | 
				
			||||||
| 
						 | 
					@ -450,6 +451,15 @@ void register_scene_types() {
 | 
				
			||||||
	ClassDB::register_class<LightmapProbe>();
 | 
						ClassDB::register_class<LightmapProbe>();
 | 
				
			||||||
	ClassDB::register_virtual_class<Lightmapper>();
 | 
						ClassDB::register_virtual_class<Lightmapper>();
 | 
				
			||||||
	ClassDB::register_class<GPUParticles3D>();
 | 
						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<CPUParticles3D>();
 | 
				
			||||||
	ClassDB::register_class<Position3D>();
 | 
						ClassDB::register_class<Position3D>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,6 +98,9 @@ void ParticlesMaterial::init_shaders() {
 | 
				
			||||||
	shader_names->sub_emitter_frequency = "sub_emitter_frequency";
 | 
						shader_names->sub_emitter_frequency = "sub_emitter_frequency";
 | 
				
			||||||
	shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
 | 
						shader_names->sub_emitter_amount_at_end = "sub_emitter_amount_at_end";
 | 
				
			||||||
	shader_names->sub_emitter_keep_velocity = "sub_emitter_keep_velocity";
 | 
						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() {
 | 
					void ParticlesMaterial::finish_shaders() {
 | 
				
			||||||
| 
						 | 
					@ -136,6 +139,10 @@ void ParticlesMaterial::_update_shader() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String code = "shader_type particles;\n";
 | 
						String code = "shader_type particles;\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (collision_scale) {
 | 
				
			||||||
 | 
							code += "render_mode collision_use_scale;\n";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	code += "uniform vec3 direction;\n";
 | 
						code += "uniform vec3 direction;\n";
 | 
				
			||||||
	code += "uniform float spread;\n";
 | 
						code += "uniform float spread;\n";
 | 
				
			||||||
	code += "uniform float flatness;\n";
 | 
						code += "uniform float flatness;\n";
 | 
				
			||||||
| 
						 | 
					@ -247,6 +254,11 @@ void ParticlesMaterial::_update_shader() {
 | 
				
			||||||
		code += "uniform sampler2D anim_offset_texture;\n";
 | 
							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
 | 
						//need a random function
 | 
				
			||||||
	code += "\n\n";
 | 
						code += "\n\n";
 | 
				
			||||||
	code += "float rand_from_seed(inout uint seed) {\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 += "		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";
 | 
							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 += "		// apply attractor forces\n";
 | 
				
			||||||
	code += "		VELOCITY += force * DELTA;\n";
 | 
						code += "		VELOCITY += force * DELTA;\n";
 | 
				
			||||||
	code += "		// orbit velocity\n";
 | 
						code += "		// orbit velocity\n";
 | 
				
			||||||
| 
						 | 
					@ -599,6 +615,13 @@ void ParticlesMaterial::_update_shader() {
 | 
				
			||||||
		code += "	VELOCITY.z = 0.0;\n";
 | 
							code += "	VELOCITY.z = 0.0;\n";
 | 
				
			||||||
		code += "	TRANSFORM[3].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) {
 | 
						if (sub_emitter_mode != SUB_EMITTER_DISABLED) {
 | 
				
			||||||
		code += "	int emit_count = 0;\n";
 | 
							code += "	int emit_count = 0;\n";
 | 
				
			||||||
		switch (sub_emitter_mode) {
 | 
							switch (sub_emitter_mode) {
 | 
				
			||||||
| 
						 | 
					@ -609,6 +632,7 @@ void ParticlesMaterial::_update_shader() {
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
			case SUB_EMITTER_AT_COLLISION: {
 | 
								case SUB_EMITTER_AT_COLLISION: {
 | 
				
			||||||
				//not implemented yet
 | 
									//not implemented yet
 | 
				
			||||||
 | 
									code += "	if (COLLIDED) emit_count = 1;\n";
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
			case SUB_EMITTER_AT_END: {
 | 
								case SUB_EMITTER_AT_END: {
 | 
				
			||||||
				//not implemented yet
 | 
									//not implemented yet
 | 
				
			||||||
| 
						 | 
					@ -1072,6 +1096,51 @@ bool ParticlesMaterial::get_sub_emitter_keep_velocity() const {
 | 
				
			||||||
	return sub_emitter_keep_velocity;
 | 
						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 {
 | 
					Shader::Mode ParticlesMaterial::get_shader_mode() const {
 | 
				
			||||||
	return Shader::MODE_PARTICLES;
 | 
						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("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_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_GROUP("Time", "");
 | 
				
			||||||
	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness");
 | 
						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::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_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_INITIAL_LINEAR_VELOCITY);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
 | 
						BIND_ENUM_CONSTANT(PARAM_ANGULAR_VELOCITY);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
 | 
						BIND_ENUM_CONSTANT(PARAM_ORBIT_VELOCITY);
 | 
				
			||||||
| 
						 | 
					@ -1283,6 +1375,12 @@ ParticlesMaterial::ParticlesMaterial() :
 | 
				
			||||||
	set_sub_emitter_amount_at_end(1);
 | 
						set_sub_emitter_amount_at_end(1);
 | 
				
			||||||
	set_sub_emitter_keep_velocity(false);
 | 
						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++) {
 | 
						for (int i = 0; i < PARAM_MAX; i++) {
 | 
				
			||||||
		set_param_randomness(Parameter(i), 0);
 | 
							set_param_randomness(Parameter(i), 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,11 +37,7 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 TODO:
 | 
					 TODO:
 | 
				
			||||||
-Path following
 | 
					-Path following
 | 
				
			||||||
*Manual emission
 | 
					 | 
				
			||||||
-Sub Emitters
 | 
					 | 
				
			||||||
-Attractors
 | 
					 | 
				
			||||||
-Emitter positions deformable by bones
 | 
					-Emitter positions deformable by bones
 | 
				
			||||||
-Collision
 | 
					 | 
				
			||||||
-Proper trails
 | 
					-Proper trails
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,6 +95,9 @@ private:
 | 
				
			||||||
			uint32_t invalid_key : 1;
 | 
								uint32_t invalid_key : 1;
 | 
				
			||||||
			uint32_t has_emission_color : 1;
 | 
								uint32_t has_emission_color : 1;
 | 
				
			||||||
			uint32_t sub_emitter : 2;
 | 
								uint32_t sub_emitter : 2;
 | 
				
			||||||
 | 
								uint32_t attractor_enabled : 1;
 | 
				
			||||||
 | 
								uint32_t collision_enabled : 1;
 | 
				
			||||||
 | 
								uint32_t collision_scale : 1;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uint32_t key;
 | 
							uint32_t key;
 | 
				
			||||||
| 
						 | 
					@ -135,6 +134,9 @@ private:
 | 
				
			||||||
		mk.emission_shape = emission_shape;
 | 
							mk.emission_shape = emission_shape;
 | 
				
			||||||
		mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
 | 
							mk.has_emission_color = emission_shape >= EMISSION_SHAPE_POINTS && emission_color_texture.is_valid();
 | 
				
			||||||
		mk.sub_emitter = sub_emitter_mode;
 | 
							mk.sub_emitter = sub_emitter_mode;
 | 
				
			||||||
 | 
							mk.collision_enabled = collision_enabled;
 | 
				
			||||||
 | 
							mk.attractor_enabled = attractor_interaction_enabled;
 | 
				
			||||||
 | 
							mk.collision_scale = collision_scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return mk;
 | 
							return mk;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -201,6 +203,9 @@ private:
 | 
				
			||||||
		StringName sub_emitter_frequency;
 | 
							StringName sub_emitter_frequency;
 | 
				
			||||||
		StringName sub_emitter_amount_at_end;
 | 
							StringName sub_emitter_amount_at_end;
 | 
				
			||||||
		StringName sub_emitter_keep_velocity;
 | 
							StringName sub_emitter_keep_velocity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							StringName collision_friction;
 | 
				
			||||||
 | 
							StringName collision_bounce;
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static ShaderNames *shader_names;
 | 
						static ShaderNames *shader_names;
 | 
				
			||||||
| 
						 | 
					@ -244,6 +249,12 @@ private:
 | 
				
			||||||
	bool sub_emitter_keep_velocity;
 | 
						bool sub_emitter_keep_velocity;
 | 
				
			||||||
	//do not save emission points here
 | 
						//do not save emission points here
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool attractor_interaction_enabled;
 | 
				
			||||||
 | 
						bool collision_enabled;
 | 
				
			||||||
 | 
						bool collision_scale;
 | 
				
			||||||
 | 
						float collision_friction;
 | 
				
			||||||
 | 
						float collision_bounce;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	static void _bind_methods();
 | 
						static void _bind_methods();
 | 
				
			||||||
	virtual void _validate_property(PropertyInfo &property) const override;
 | 
						virtual void _validate_property(PropertyInfo &property) const override;
 | 
				
			||||||
| 
						 | 
					@ -298,6 +309,21 @@ public:
 | 
				
			||||||
	void set_lifetime_randomness(float p_lifetime);
 | 
						void set_lifetime_randomness(float p_lifetime);
 | 
				
			||||||
	float get_lifetime_randomness() const;
 | 
						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 init_shaders();
 | 
				
			||||||
	static void finish_shaders();
 | 
						static void finish_shaders();
 | 
				
			||||||
	static void flush_changes();
 | 
						static void flush_changes();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_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(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_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_scene_pass(uint64_t p_pass) = 0;
 | 
				
			||||||
	virtual void set_time(double p_time, double p_step) = 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_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_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_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_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_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;
 | 
						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_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 */
 | 
						/* GLOBAL VARIABLES */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) = 0;
 | 
						virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value) = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					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");
 | 
						RENDER_TIMESTAMP("Setup Rendering Material");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_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_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_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:
 | 
					public:
 | 
				
			||||||
	virtual void set_time(double p_time, double p_step);
 | 
						virtual void set_time(double p_time, double p_step);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					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);
 | 
						RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
 | 
				
			||||||
	ERR_FAIL_COND(!rb);
 | 
						ERR_FAIL_COND(!rb);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_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_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_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);
 | 
						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);
 | 
						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(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_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) {
 | 
						virtual void set_scene_pass(uint64_t p_pass) {
 | 
				
			||||||
		scene_pass = p_pass;
 | 
							scene_pass = p_pass;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3333,6 +3333,10 @@ void RasterizerStorageRD::_particles_free_data(Particles *particles) {
 | 
				
			||||||
	particles->particles_transforms_buffer_uniform_set = RID();
 | 
						particles->particles_transforms_buffer_uniform_set = RID();
 | 
				
			||||||
	particles->particle_buffer = 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()) {
 | 
						if (particles->particles_sort_buffer.is_valid()) {
 | 
				
			||||||
		RD::get_singleton()->free(particles->particles_sort_buffer);
 | 
							RD::get_singleton()->free(particles->particles_sort_buffer);
 | 
				
			||||||
		particles->particles_sort_buffer = RID();
 | 
							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;
 | 
						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) {
 | 
					void RasterizerStorageRD::particles_set_process_material(RID p_particles, RID p_material) {
 | 
				
			||||||
	Particles *particles = particles_owner.getornull(p_particles);
 | 
						Particles *particles = particles_owner.getornull(p_particles);
 | 
				
			||||||
	ERR_FAIL_COND(!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];
 | 
						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) {
 | 
					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)) {
 | 
						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;
 | 
							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;
 | 
						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;
 | 
						ParticlesShader::PushConstant push_constant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	push_constant.clear = p_particles->clear;
 | 
						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_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, 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->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()) {
 | 
						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));
 | 
						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() {
 | 
					RasterizerStorageRD::ParticlesMaterialData::~ParticlesMaterialData() {
 | 
				
			||||||
| 
						 | 
					@ -4211,6 +4429,171 @@ RasterizerStorageRD::MaterialData *RasterizerStorageRD::_create_particles_materi
 | 
				
			||||||
	return material_data;
 | 
						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 */
 | 
					/* SKELETON API */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RID RasterizerStorageRD::skeleton_create() {
 | 
					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);
 | 
						ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
 | 
				
			||||||
	ERR_FAIL_COND(!reflection_probe);
 | 
						ERR_FAIL_COND(!reflection_probe);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (reflection_probe->extents == p_extents) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	reflection_probe->extents = p_extents;
 | 
						reflection_probe->extents = p_extents;
 | 
				
			||||||
	reflection_probe->instance_dependency.instance_notify_changed(true, false);
 | 
						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)) {
 | 
						} else if (particles_owner.owns(p_base)) {
 | 
				
			||||||
		Particles *p = particles_owner.getornull(p_base);
 | 
							Particles *p = particles_owner.getornull(p_base);
 | 
				
			||||||
		p_instance->update_dependency(&p->instance_dependency);
 | 
							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)) {
 | 
						if (particles_owner.owns(p_rid)) {
 | 
				
			||||||
		return RS::INSTANCE_PARTICLES;
 | 
							return RS::INSTANCE_PARTICLES;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (particles_collision_owner.owns(p_rid)) {
 | 
				
			||||||
 | 
							return RS::INSTANCE_PARTICLES_COLLISION;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return RS::INSTANCE_NONE;
 | 
						return RS::INSTANCE_NONE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6735,8 +7127,6 @@ void RasterizerStorageRD::update_dirty_resources() {
 | 
				
			||||||
	_update_dirty_multimeshes();
 | 
						_update_dirty_multimeshes();
 | 
				
			||||||
	_update_dirty_skeletons();
 | 
						_update_dirty_skeletons();
 | 
				
			||||||
	_update_decal_atlas();
 | 
						_update_decal_atlas();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	update_particles();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool RasterizerStorageRD::has_os_feature(const String &p_feature) const {
 | 
					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_free_data(particles);
 | 
				
			||||||
		particles->instance_dependency.instance_notify_deleted(p_rid);
 | 
							particles->instance_dependency.instance_notify_deleted(p_rid);
 | 
				
			||||||
		particles_owner.free(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)) {
 | 
						} else if (render_target_owner.owns(p_rid)) {
 | 
				
			||||||
		RenderTarget *rt = render_target_owner.getornull(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_COLOR"] = "restart_color";
 | 
				
			||||||
		actions.renames["RESTART_CUSTOM"] = "restart_custom";
 | 
							actions.renames["RESTART_CUSTOM"] = "restart_custom";
 | 
				
			||||||
		actions.renames["emit_particle"] = "emit_particle";
 | 
							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_force"] = "#define DISABLE_FORCE\n";
 | 
				
			||||||
		actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\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["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.sampler_array_name = "material_samplers";
 | 
				
			||||||
		actions.base_texture_binding_index = 1;
 | 
							actions.base_texture_binding_index = 1;
 | 
				
			||||||
		actions.texture_layout_set = 2;
 | 
							actions.texture_layout_set = 3;
 | 
				
			||||||
		actions.base_uniform_string = "material.";
 | 
							actions.base_uniform_string = "material.";
 | 
				
			||||||
		actions.base_varying_index = 10;
 | 
							actions.base_varying_index = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -475,6 +475,46 @@ private:
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ParticlesFrameParams {
 | 
						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;
 | 
							uint32_t emitting;
 | 
				
			||||||
		float system_phase;
 | 
							float system_phase;
 | 
				
			||||||
		float prev_system_phase;
 | 
							float prev_system_phase;
 | 
				
			||||||
| 
						 | 
					@ -486,9 +526,14 @@ private:
 | 
				
			||||||
		float delta;
 | 
							float delta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uint32_t random_seed;
 | 
							uint32_t random_seed;
 | 
				
			||||||
		uint32_t pad[3];
 | 
							uint32_t attractor_count;
 | 
				
			||||||
 | 
							uint32_t collider_count;
 | 
				
			||||||
 | 
							float particle_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		float emission_transform[16];
 | 
							float emission_transform[16];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Attractor attractors[MAX_ATTRACTORS];
 | 
				
			||||||
 | 
							Collider colliders[MAX_COLLIDERS];
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ParticleEmissionBufferData {
 | 
						struct ParticleEmissionBufferData {
 | 
				
			||||||
| 
						 | 
					@ -536,6 +581,11 @@ private:
 | 
				
			||||||
		RID particles_material_uniform_set;
 | 
							RID particles_material_uniform_set;
 | 
				
			||||||
		RID particles_copy_uniform_set;
 | 
							RID particles_copy_uniform_set;
 | 
				
			||||||
		RID particles_transforms_buffer_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_buffer;
 | 
				
			||||||
		RID particles_sort_uniform_set;
 | 
							RID particles_sort_uniform_set;
 | 
				
			||||||
| 
						 | 
					@ -557,6 +607,7 @@ private:
 | 
				
			||||||
		int fixed_fps;
 | 
							int fixed_fps;
 | 
				
			||||||
		bool fractional_delta;
 | 
							bool fractional_delta;
 | 
				
			||||||
		float frame_remainder;
 | 
							float frame_remainder;
 | 
				
			||||||
 | 
							float collision_base_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool clear;
 | 
							bool clear;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -569,6 +620,8 @@ private:
 | 
				
			||||||
		ParticleEmissionBuffer *emission_buffer = nullptr;
 | 
							ParticleEmissionBuffer *emission_buffer = nullptr;
 | 
				
			||||||
		RID emission_storage_buffer;
 | 
							RID emission_storage_buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Set<RasterizerScene::InstanceBase *> collisions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Particles() :
 | 
							Particles() :
 | 
				
			||||||
				inactive(true),
 | 
									inactive(true),
 | 
				
			||||||
				inactive_time(0.0),
 | 
									inactive_time(0.0),
 | 
				
			||||||
| 
						 | 
					@ -590,6 +643,7 @@ private:
 | 
				
			||||||
				fixed_fps(0),
 | 
									fixed_fps(0),
 | 
				
			||||||
				fractional_delta(false),
 | 
									fractional_delta(false),
 | 
				
			||||||
				frame_remainder(0),
 | 
									frame_remainder(0),
 | 
				
			||||||
 | 
									collision_base_size(0.01),
 | 
				
			||||||
				clear(true) {
 | 
									clear(true) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -704,6 +758,28 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutable RID_Owner<Particles> particles_owner;
 | 
						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 */
 | 
						/* Skeleton */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct Skeleton {
 | 
						struct Skeleton {
 | 
				
			||||||
| 
						 | 
					@ -1691,6 +1767,7 @@ public:
 | 
				
			||||||
	void particles_set_process_material(RID p_particles, RID p_material);
 | 
						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_fixed_fps(RID p_particles, int p_fps);
 | 
				
			||||||
	void particles_set_fractional_delta(RID p_particles, bool p_enable);
 | 
						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_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_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);
 | 
						void particles_set_subemitter(RID p_particles, RID p_subemitter_particles);
 | 
				
			||||||
| 
						 | 
					@ -1748,6 +1825,27 @@ public:
 | 
				
			||||||
		return particles->particles_transforms_buffer_uniform_set;
 | 
							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 */
 | 
						/* GLOBAL VARIABLES API */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value);
 | 
						virtual void global_variable_add(const StringName &p_name, RS::GlobalVariableType p_type, const Variant &p_value);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,40 @@ global_variables;
 | 
				
			||||||
/* Set 1: FRAME AND PARTICLE DATA */
 | 
					/* Set 1: FRAME AND PARTICLE DATA */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// a frame history is kept for trail deterministic behavior
 | 
					// 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 {
 | 
					struct FrameParams {
 | 
				
			||||||
	bool emitting;
 | 
						bool emitting;
 | 
				
			||||||
	float system_phase;
 | 
						float system_phase;
 | 
				
			||||||
| 
						 | 
					@ -43,9 +77,14 @@ struct FrameParams {
 | 
				
			||||||
	float delta;
 | 
						float delta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint random_seed;
 | 
						uint random_seed;
 | 
				
			||||||
	uint pad[3];
 | 
						uint attractor_count;
 | 
				
			||||||
 | 
						uint collider_count;
 | 
				
			||||||
 | 
						float particle_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mat4 emission_transform;
 | 
						mat4 emission_transform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Attractor attractors[MAX_ATTRACTORS];
 | 
				
			||||||
 | 
						Collider colliders[MAX_COLLIDERS];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
 | 
					layout(set = 1, binding = 0, std430) restrict buffer FrameHistory {
 | 
				
			||||||
| 
						 | 
					@ -80,7 +119,7 @@ struct ParticleEmission {
 | 
				
			||||||
	vec4 custom;
 | 
						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;
 | 
						int particle_count;
 | 
				
			||||||
	uint pad0;
 | 
						uint pad0;
 | 
				
			||||||
	uint pad1;
 | 
						uint pad1;
 | 
				
			||||||
| 
						 | 
					@ -89,7 +128,7 @@ layout(set = 1, binding = 2, std430) restrict volatile coherent buffer SourceEmi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
src_particles;
 | 
					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_count;
 | 
				
			||||||
	int particle_max;
 | 
						int particle_max;
 | 
				
			||||||
	uint pad1;
 | 
						uint pad1;
 | 
				
			||||||
| 
						 | 
					@ -98,10 +137,17 @@ layout(set = 1, binding = 3, std430) restrict volatile coherent buffer DestEmiss
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
dst_particles;
 | 
					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
 | 
					#ifdef USE_MATERIAL_UNIFORMS
 | 
				
			||||||
layout(set = 2, binding = 0, std140) uniform MaterialUniforms{
 | 
					layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
 | 
				
			||||||
	/* clang-format off */
 | 
						/* clang-format off */
 | 
				
			||||||
MATERIAL_UNIFORMS
 | 
					MATERIAL_UNIFORMS
 | 
				
			||||||
	/* clang-format on */
 | 
						/* 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);
 | 
							atomicAdd(dst_particles.particle_count, -1);
 | 
				
			||||||
		return false;
 | 
							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].xform = p_xform;
 | 
				
			||||||
	dst_particles.data[dst_index].velocity = p_velocity;
 | 
						dst_particles.data[dst_index].velocity = p_velocity;
 | 
				
			||||||
	dst_particles.data[dst_index].color = p_color;
 | 
						dst_particles.data[dst_index].color = p_color;
 | 
				
			||||||
| 
						 | 
					@ -217,6 +241,199 @@ void main() {
 | 
				
			||||||
				vec4(0.0, 0.0, 0.0, 1.0));
 | 
									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 (params.sub_emitter_mode) {
 | 
				
			||||||
		if (!PARTICLE.is_active) {
 | 
							if (!PARTICLE.is_active) {
 | 
				
			||||||
			int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
 | 
								int src_index = atomicAdd(src_particles.particle_count, -1) - 1;
 | 
				
			||||||
| 
						 | 
					@ -329,66 +546,4 @@ COMPUTE_SHADER_CODE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* clang-format on */
 | 
							/* 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
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,6 +108,9 @@ void RenderingServerRaster::draw(bool p_swap_buffers, double frame_step) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RSG::scene->update_dirty_instances(); //update scene stuff
 | 
						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::scene->render_probes();
 | 
				
			||||||
	RSG::viewport->draw_viewports();
 | 
						RSG::viewport->draw_viewports();
 | 
				
			||||||
	RSG::canvas_render->update();
 | 
						RSG::canvas_render->update();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -452,6 +452,7 @@ public:
 | 
				
			||||||
	BIND1(particles_restart, RID)
 | 
						BIND1(particles_restart, RID)
 | 
				
			||||||
	BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
 | 
						BIND6(particles_emit, RID, const Transform &, const Vector3 &, const Color &, const Color &, uint32_t)
 | 
				
			||||||
	BIND2(particles_set_subemitter, RID, RID)
 | 
						BIND2(particles_set_subemitter, RID, RID)
 | 
				
			||||||
 | 
						BIND2(particles_set_collision_base_size, RID, float)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
 | 
						BIND2(particles_set_draw_order, RID, RS::ParticlesDrawOrder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -461,6 +462,21 @@ public:
 | 
				
			||||||
	BIND1R(AABB, particles_get_current_aabb, RID)
 | 
						BIND1R(AABB, particles_get_current_aabb, RID)
 | 
				
			||||||
	BIND2(particles_set_emission_transform, RID, const Transform &)
 | 
						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
 | 
					#undef BINDBASE
 | 
				
			||||||
//from now on, calls forwarded to this singleton
 | 
					//from now on, calls forwarded to this singleton
 | 
				
			||||||
#define BINDBASE RSG::scene
 | 
					#define BINDBASE RSG::scene
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
						} else if (B->base_type == RS::INSTANCE_GI_PROBE && A->base_type == RS::INSTANCE_LIGHT) {
 | 
				
			||||||
		InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
 | 
							InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(B->base_data);
 | 
				
			||||||
		return gi_probe->lights.insert(A);
 | 
							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;
 | 
						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);
 | 
							Set<Instance *>::Element *E = reinterpret_cast<Set<Instance *>::Element *>(udata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gi_probe->lights.erase(E);
 | 
							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);
 | 
									RSG::scene_render->reflection_probe_release_atlas_index(reflection_probe->instance);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
 | 
								case RS::INSTANCE_PARTICLES_COLLISION: {
 | 
				
			||||||
 | 
									heightfield_particle_colliders_update_list.erase(instance);
 | 
				
			||||||
 | 
								} break;
 | 
				
			||||||
			case RS::INSTANCE_GI_PROBE: {
 | 
								case RS::INSTANCE_GI_PROBE: {
 | 
				
			||||||
				InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
 | 
									InstanceGIProbeData *gi_probe = static_cast<InstanceGIProbeData *>(instance->base_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -701,6 +708,12 @@ void RenderingServerScene::instance_set_visible(RID p_instance, bool p_visible)
 | 
				
			||||||
				instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_GI_PROBE, p_visible ? (RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT)) : 0);
 | 
									instance->scenario->octree.set_pairable(instance->octree_id, p_visible, 1 << RS::INSTANCE_GI_PROBE, p_visible ? (RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT)) : 0);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} 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;
 | 
							} break;
 | 
				
			||||||
		default: {
 | 
							default: {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -1026,6 +1039,13 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
 | 
				
			||||||
		RSG::storage->particles_set_emission_transform(p_instance->base, p_instance->transform);
 | 
							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()) {
 | 
						if (p_instance->aabb.has_no_surface()) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1085,6 +1105,11 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
 | 
				
			||||||
			pairable = true;
 | 
								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) {
 | 
							if (p_instance->base_type == RS::INSTANCE_GI_PROBE) {
 | 
				
			||||||
			//lights and geometries
 | 
								//lights and geometries
 | 
				
			||||||
			pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT) : 0;
 | 
								pairable_mask = p_instance->visible ? RS::INSTANCE_GEOMETRY_MASK | (1 << RS::INSTANCE_LIGHT) : 0;
 | 
				
			||||||
| 
						 | 
					@ -1145,6 +1170,10 @@ void RenderingServerScene::_update_instance_aabb(Instance *p_instance) {
 | 
				
			||||||
				new_aabb = RSG::storage->particles_get_aabb(p_instance->base);
 | 
									new_aabb = RSG::storage->particles_get_aabb(p_instance->base);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} break;
 | 
				
			||||||
 | 
							case RenderingServer::INSTANCE_PARTICLES_COLLISION: {
 | 
				
			||||||
 | 
								new_aabb = RSG::storage->particles_collision_get_aabb(p_instance->base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} break;
 | 
							} break;
 | 
				
			||||||
		case RenderingServer::INSTANCE_LIGHT: {
 | 
							case RenderingServer::INSTANCE_LIGHT: {
 | 
				
			||||||
			new_aabb = RSG::storage->light_get_aabb(p_instance->base);
 | 
								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) {
 | 
					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;
 | 
						List<RasterizerStorage::InstanceShaderParam> plist;
 | 
				
			||||||
	RSG::storage->material_get_instance_shader_parameters(p_material, &plist);
 | 
						RSG::storage->material_get_instance_shader_parameters(p_material, &plist);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -385,6 +385,8 @@ public:
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Set<Instance *> heightfield_particle_colliders_update_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int instance_cull_count;
 | 
						int instance_cull_count;
 | 
				
			||||||
	Instance *instance_cull_result[MAX_INSTANCE_CULL];
 | 
						Instance *instance_cull_result[MAX_INSTANCE_CULL];
 | 
				
			||||||
	Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
 | 
						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 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 update_dirty_instances();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void render_particle_colliders();
 | 
				
			||||||
	void render_probes();
 | 
						void render_probes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size);
 | 
						TypedArray<Image> bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,6 +124,7 @@ void RenderingServerWrapMT::finish() {
 | 
				
			||||||
	gi_probe_free_cached_ids();
 | 
						gi_probe_free_cached_ids();
 | 
				
			||||||
	lightmap_free_cached_ids();
 | 
						lightmap_free_cached_ids();
 | 
				
			||||||
	particles_free_cached_ids();
 | 
						particles_free_cached_ids();
 | 
				
			||||||
 | 
						particles_collision_free_cached_ids();
 | 
				
			||||||
	camera_free_cached_ids();
 | 
						camera_free_cached_ids();
 | 
				
			||||||
	viewport_free_cached_ids();
 | 
						viewport_free_cached_ids();
 | 
				
			||||||
	environment_free_cached_ids();
 | 
						environment_free_cached_ids();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -356,6 +356,8 @@ public:
 | 
				
			||||||
	FUNC2(particles_set_process_material, RID, RID)
 | 
						FUNC2(particles_set_process_material, RID, RID)
 | 
				
			||||||
	FUNC2(particles_set_fixed_fps, RID, int)
 | 
						FUNC2(particles_set_fixed_fps, RID, int)
 | 
				
			||||||
	FUNC2(particles_set_fractional_delta, RID, bool)
 | 
						FUNC2(particles_set_fractional_delta, RID, bool)
 | 
				
			||||||
 | 
						FUNC2(particles_set_collision_base_size, RID, float)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FUNC1R(bool, particles_is_inactive, RID)
 | 
						FUNC1R(bool, particles_is_inactive, RID)
 | 
				
			||||||
	FUNC1(particles_request_process, RID)
 | 
						FUNC1(particles_request_process, RID)
 | 
				
			||||||
	FUNC1(particles_restart, RID)
 | 
						FUNC1(particles_restart, RID)
 | 
				
			||||||
| 
						 | 
					@ -371,6 +373,21 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FUNC1R(AABB, particles_get_current_aabb, RID)
 | 
						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 */
 | 
						/* CAMERA API */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	FUNCRID(camera)
 | 
						FUNCRID(camera)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_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_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["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;
 | 
						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].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_force");
 | 
				
			||||||
	shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
 | 
						shader_modes[RS::SHADER_PARTICLES].modes.push_back("disable_velocity");
 | 
				
			||||||
	shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data");
 | 
						shader_modes[RS::SHADER_PARTICLES].modes.push_back("keep_data");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -578,6 +578,7 @@ public:
 | 
				
			||||||
	virtual void particles_set_process_material(RID p_particles, RID p_material) = 0;
 | 
						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_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_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 bool particles_is_inactive(RID p_particles) = 0;
 | 
				
			||||||
	virtual void particles_request_process(RID p_particles) = 0;
 | 
						virtual void particles_request_process(RID p_particles) = 0;
 | 
				
			||||||
	virtual void particles_restart(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
 | 
						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 */
 | 
						/* CAMERA API */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual RID camera_create() = 0;
 | 
						virtual RID camera_create() = 0;
 | 
				
			||||||
| 
						 | 
					@ -965,6 +1003,7 @@ public:
 | 
				
			||||||
		INSTANCE_MULTIMESH,
 | 
							INSTANCE_MULTIMESH,
 | 
				
			||||||
		INSTANCE_IMMEDIATE,
 | 
							INSTANCE_IMMEDIATE,
 | 
				
			||||||
		INSTANCE_PARTICLES,
 | 
							INSTANCE_PARTICLES,
 | 
				
			||||||
 | 
							INSTANCE_PARTICLES_COLLISION,
 | 
				
			||||||
		INSTANCE_LIGHT,
 | 
							INSTANCE_LIGHT,
 | 
				
			||||||
		INSTANCE_REFLECTION_PROBE,
 | 
							INSTANCE_REFLECTION_PROBE,
 | 
				
			||||||
		INSTANCE_DECAL,
 | 
							INSTANCE_DECAL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue