summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2020-10-09 19:28:29 +0200
committerGitHub <noreply@github.com>2020-10-09 19:28:29 +0200
commitb724d3851a49b033a8bc24bc1c73a3aa29eb0390 (patch)
tree7d20274c657c5f154186b690c1c0a67ca0174a9f /editor
parentc35005ba25473ea8fa48aadbd1687984c76457cf (diff)
parent26f5bd245c535fec5bfdd51a0f939d0a51179d85 (diff)
Merge pull request #42628 from reduz/particle-collisions
Implement GPU Particle Collisions
Diffstat (limited to 'editor')
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/import/resource_importer_layered_texture.cpp2
-rw-r--r--editor/node_3d_editor_gizmos.cpp261
-rw-r--r--editor/node_3d_editor_gizmos.h17
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp201
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h74
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp1
7 files changed, 557 insertions, 1 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 8edda3123b..73562e0695 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -128,6 +128,7 @@
#include "editor/plugins/gi_probe_editor_plugin.h"
#include "editor/plugins/gpu_particles_2d_editor_plugin.h"
#include "editor/plugins/gpu_particles_3d_editor_plugin.h"
+#include "editor/plugins/gpu_particles_collision_sdf_editor_plugin.h"
#include "editor/plugins/gradient_editor_plugin.h"
#include "editor/plugins/item_list_editor_plugin.h"
#include "editor/plugins/light_occluder_2d_editor_plugin.h"
@@ -6635,6 +6636,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(PhysicalBone3DEditorPlugin(this)));
add_editor_plugin(memnew(MeshEditorPlugin(this)));
add_editor_plugin(memnew(MaterialEditorPlugin(this)));
+ add_editor_plugin(memnew(GPUParticlesCollisionSDFEditorPlugin(this)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i, this));
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index bbf62596d0..ac068c05cf 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -51,7 +51,7 @@ String ResourceImporterLayeredTexture::get_importer_name() const {
return "cubemap_array_texture";
} break;
case MODE_3D: {
- return "cubemap_3d_texture";
+ return "3d_texture";
} break;
}
diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp
index 6e5fb6389d..397a958d8f 100644
--- a/editor/node_3d_editor_gizmos.cpp
+++ b/editor/node_3d_editor_gizmos.cpp
@@ -41,6 +41,7 @@
#include "scene/3d/decal.h"
#include "scene/3d/gi_probe.h"
#include "scene/3d/gpu_particles_3d.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_probe.h"
#include "scene/3d/listener_3d.h"
@@ -2455,6 +2456,266 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
////
+////
+
+GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1));
+ create_material("shape_material", gizmo_color);
+ gizmo_color.a = 0.15;
+ create_material("shape_material_internal", gizmo_color);
+
+ create_handle_material("handles");
+}
+
+bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return (Object::cast_to<GPUParticlesCollision3D>(p_spatial) != nullptr) || (Object::cast_to<GPUParticlesAttractor3D>(p_spatial) != nullptr);
+}
+
+String GPUParticlesCollision3DGizmoPlugin::get_name() const {
+ return "GPUParticlesCollision3D";
+}
+
+int GPUParticlesCollision3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+ const Node3D *cs = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ return "Radius";
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ return "Extents";
+ }
+
+ return "";
+}
+
+Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
+ const Node3D *cs = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ return p_gizmo->get_spatial_node()->call("get_radius");
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ return Vector3(p_gizmo->get_spatial_node()->call("get_extents"));
+ }
+
+ return Variant();
+}
+
+void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ Transform gt = sn->get_global_transform();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ float d = ra.x;
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ sn->call("set_radius", d);
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ Vector3 axis;
+ axis[p_idx] = 1.0;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_idx];
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ Vector3 he = sn->call("get_extents");
+ he[p_idx] = d;
+ sn->call("set_extents", he);
+ }
+}
+
+void GPUParticlesCollision3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ if (p_cancel) {
+ sn->call("set_radius", p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Radius"));
+ ur->add_do_method(sn, "set_radius", sn->call("get_radius"));
+ ur->add_undo_method(sn, "set_radius", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ if (p_cancel) {
+ sn->call("set_extents", p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Box Shape Extents"));
+ ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
+ ur->add_undo_method(sn, "set_extents", p_restore);
+ ur->commit_action();
+ }
+}
+
+void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Node3D *cs = p_gizmo->get_spatial_node();
+
+ print_line("redraw request " + itos(cs != nullptr));
+ p_gizmo->clear();
+
+ const Ref<Material> material =
+ get_material("shape_material", p_gizmo);
+ const Ref<Material> material_internal =
+ get_material("shape_material_internal", p_gizmo);
+
+ Ref<Material> handles_material = get_material("handles");
+
+ if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ float r = cs->call("get_radius");
+
+ Vector<Vector3> points;
+
+ for (int i = 0; i <= 360; i++) {
+ float ra = Math::deg2rad((float)i);
+ float rb = Math::deg2rad((float)i + 1);
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
+
+ points.push_back(Vector3(a.x, 0, a.y));
+ points.push_back(Vector3(b.x, 0, b.y));
+ points.push_back(Vector3(0, a.x, a.y));
+ points.push_back(Vector3(0, b.x, b.y));
+ points.push_back(Vector3(a.x, a.y, 0));
+ points.push_back(Vector3(b.x, b.y, 0));
+ }
+
+ Vector<Vector3> collision_segments;
+
+ for (int i = 0; i < 64; i++) {
+ float ra = i * Math_PI * 2.0 / 64.0;
+ float rb = (i + 1) * Math_PI * 2.0 / 64.0;
+ Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r;
+ Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r;
+
+ collision_segments.push_back(Vector3(a.x, 0, a.y));
+ collision_segments.push_back(Vector3(b.x, 0, b.y));
+ collision_segments.push_back(Vector3(0, a.x, a.y));
+ collision_segments.push_back(Vector3(0, b.x, b.y));
+ collision_segments.push_back(Vector3(a.x, a.y, 0));
+ collision_segments.push_back(Vector3(b.x, b.y, 0));
+ }
+
+ p_gizmo->add_lines(points, material);
+ p_gizmo->add_collision_segments(collision_segments);
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r, 0, 0));
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
+ if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ Vector<Vector3> lines;
+ AABB aabb;
+ aabb.position = -cs->call("get_extents").operator Vector3();
+ aabb.size = aabb.position * -2;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = cs->call("get_extents").operator Vector3()[i];
+ handles.push_back(ax);
+ }
+
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+ p_gizmo->add_handles(handles, handles_material);
+
+ GPUParticlesCollisionSDF *col_sdf = Object::cast_to<GPUParticlesCollisionSDF>(cs);
+ if (col_sdf) {
+ static const int subdivs[GPUParticlesCollisionSDF::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ int subdiv = subdivs[col_sdf->get_resolution()];
+ float cell_size = aabb.get_longest_axis_size() / subdiv;
+
+ lines.clear();
+
+ for (int i = 1; i < subdiv; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (cell_size * i > aabb.size[j]) {
+ continue;
+ }
+
+ Vector2 dir;
+ dir[j] = 1.0;
+ Vector2 ta, tb;
+ int j_n1 = (j + 1) % 3;
+ int j_n2 = (j + 2) % 3;
+ ta[j_n1] = 1.0;
+ tb[j_n2] = 1.0;
+
+ for (int k = 0; k < 4; k++) {
+ Vector3 from = aabb.position, to = aabb.position;
+ from[j] += cell_size * i;
+ to[j] += cell_size * i;
+
+ if (k & 1) {
+ to[j_n1] += aabb.size[j_n1];
+ } else {
+ to[j_n2] += aabb.size[j_n2];
+ }
+
+ if (k & 2) {
+ from[j_n1] += aabb.size[j_n1];
+ from[j_n2] += aabb.size[j_n2];
+ }
+
+ lines.push_back(from);
+ lines.push_back(to);
+ }
+ }
+ }
+
+ p_gizmo->add_lines(lines, material_internal);
+ }
+ }
+}
+
+/////
+
+////
+
ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5));
diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h
index c7aae39a45..4826054643 100644
--- a/editor/node_3d_editor_gizmos.h
+++ b/editor/node_3d_editor_gizmos.h
@@ -253,6 +253,23 @@ public:
GPUParticles3DGizmoPlugin();
};
+class GPUParticlesCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(GPUParticlesCollision3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_name() const override;
+ int get_priority() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override;
+ void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override;
+ void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override;
+
+ GPUParticlesCollision3DGizmoPlugin();
+};
+
class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
new file mode 100644
index 0000000000..0288645f91
--- /dev/null
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -0,0 +1,201 @@
+/*************************************************************************/
+/* gpu_particles_collision_sdf_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gpu_particles_collision_sdf_editor_plugin.h"
+
+void GPUParticlesCollisionSDFEditorPlugin::_bake() {
+ if (col_sdf) {
+ if (col_sdf->get_texture().is_null() || !col_sdf->get_texture()->get_path().is_resource_file()) {
+ String path = get_tree()->get_edited_scene_root()->get_filename();
+ if (path == String()) {
+ path = "res://" + col_sdf->get_name() + "_data.exr";
+ } else {
+ String ext = path.get_extension();
+ path = path.get_basename() + "." + col_sdf->get_name() + "_data.exr";
+ }
+ probe_file->set_current_path(path);
+ probe_file->popup_file_dialog();
+ return;
+ }
+
+ _sdf_save_path_and_bake(col_sdf->get_texture()->get_path());
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::edit(Object *p_object) {
+ GPUParticlesCollisionSDF *s = Object::cast_to<GPUParticlesCollisionSDF>(p_object);
+ if (!s) {
+ return;
+ }
+
+ col_sdf = s;
+}
+
+bool GPUParticlesCollisionSDFEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("GPUParticlesCollisionSDF");
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PROCESS) {
+ if (!col_sdf) {
+ return;
+ }
+
+ const Vector3i size = col_sdf->get_estimated_cell_size();
+ String text = vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z);
+ int data_size = 2;
+
+ const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
+ text += " - " + vformat(TTR("VRAM Size: %s MB"), String::num(size_mb, 2));
+
+ if (bake_info->get_text() == text) {
+ return;
+ }
+
+ // Color the label depending on the estimated performance level.
+ Color color;
+ if (size_mb <= 16.0 + CMP_EPSILON) {
+ // Fast.
+ color = bake_info->get_theme_color("success_color", "Editor");
+ } else if (size_mb <= 64.0 + CMP_EPSILON) {
+ // Medium.
+ color = bake_info->get_theme_color("warning_color", "Editor");
+ } else {
+ // Slow.
+ color = bake_info->get_theme_color("error_color", "Editor");
+ }
+ bake_info->add_theme_color_override("font_color", color);
+
+ bake_info->set_text(text);
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ bake_hb->show();
+ set_process(true);
+ } else {
+ bake_hb->hide();
+ set_process(false);
+ }
+}
+
+EditorProgress *GPUParticlesCollisionSDFEditorPlugin::tmp_progress = nullptr;
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_begin(int p_steps) {
+ ERR_FAIL_COND(tmp_progress != nullptr);
+
+ tmp_progress = memnew(EditorProgress("bake_sdf", TTR("Bake SDF"), p_steps));
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_step(int p_step, const String &p_description) {
+ ERR_FAIL_COND(tmp_progress == nullptr);
+ tmp_progress->step(p_description, p_step, false);
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::bake_func_end() {
+ ERR_FAIL_COND(tmp_progress == nullptr);
+ memdelete(tmp_progress);
+ tmp_progress = nullptr;
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake(const String &p_path) {
+ probe_file->hide();
+ if (col_sdf) {
+ Ref<Image> bake_img = col_sdf->bake();
+ if (bake_img.is_null()) {
+ EditorNode::get_singleton()->show_warning("Bake Error.");
+ return;
+ }
+
+ Ref<ConfigFile> config;
+
+ config.instance();
+ if (FileAccess::exists(p_path + ".import")) {
+ config->load(p_path + ".import");
+ }
+
+ config->set_value("remap", "importer", "3d_texture");
+ config->set_value("remap", "type", "StreamTexture3D");
+ if (!config->has_section_key("params", "compress/mode")) {
+ config->set_value("params", "compress/mode", 3); //user may want another compression, so leave it be
+ }
+ config->set_value("params", "compress/channel_pack", 1);
+ config->set_value("params", "mipmaps/generate", false);
+ config->set_value("params", "slices/horizontal", 1);
+ config->set_value("params", "slices/vertical", bake_img->get_meta("depth"));
+
+ config->save(p_path + ".import");
+
+ Error err = bake_img->save_exr(p_path, false);
+ ERR_FAIL_COND(err);
+ ResourceLoader::import(p_path);
+ Ref<Texture> t = ResourceLoader::load(p_path); //if already loaded, it will be updated on refocus?
+ ERR_FAIL_COND(t.is_null());
+
+ col_sdf->set_texture(t);
+ }
+}
+
+void GPUParticlesCollisionSDFEditorPlugin::_bind_methods() {
+}
+
+GPUParticlesCollisionSDFEditorPlugin::GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ bake_hb = memnew(HBoxContainer);
+ bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bake_hb->hide();
+ bake = memnew(Button);
+ bake->set_flat(true);
+ bake->set_icon(editor->get_gui_base()->get_theme_icon("Bake", "EditorIcons"));
+ bake->set_text(TTR("Bake SDF"));
+ bake->connect("pressed", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_bake));
+ bake_hb->add_child(bake);
+ bake_info = memnew(Label);
+ bake_info->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ bake_info->set_clip_text(true);
+ bake_hb->add_child(bake_info);
+
+ add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
+ col_sdf = nullptr;
+ probe_file = memnew(EditorFileDialog);
+ probe_file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ probe_file->add_filter("*.exr");
+ probe_file->connect("file_selected", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake));
+ get_editor_interface()->get_base_control()->add_child(probe_file);
+ probe_file->set_title(TTR("Select path for SDF Texture"));
+
+ GPUParticlesCollisionSDF::bake_begin_function = bake_func_begin;
+ GPUParticlesCollisionSDF::bake_step_function = bake_func_step;
+ GPUParticlesCollisionSDF::bake_end_function = bake_func_end;
+}
+
+GPUParticlesCollisionSDFEditorPlugin::~GPUParticlesCollisionSDFEditorPlugin() {
+}
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
new file mode 100644
index 0000000000..0cdc70a62b
--- /dev/null
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* gpu_particles_collision_sdf_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
+#define GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/gpu_particles_collision_3d.h"
+#include "scene/resources/material.h"
+
+class GPUParticlesCollisionSDFEditorPlugin : public EditorPlugin {
+ GDCLASS(GPUParticlesCollisionSDFEditorPlugin, EditorPlugin);
+
+ GPUParticlesCollisionSDF *col_sdf;
+
+ HBoxContainer *bake_hb;
+ Label *bake_info;
+ Button *bake;
+ EditorNode *editor;
+
+ EditorFileDialog *probe_file;
+
+ static EditorProgress *tmp_progress;
+ static void bake_func_begin(int p_steps);
+ static void bake_func_step(int p_step, const String &p_description);
+ static void bake_func_end();
+
+ void _bake();
+ void _sdf_save_path_and_bake(const String &p_path);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual String get_name() const override { return "GPUParticlesCollisionSDF"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node);
+ ~GPUParticlesCollisionSDFEditorPlugin();
+};
+
+#endif // GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index b2fd855834..8bd6e39252 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -6092,6 +6092,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<VehicleWheel3DGizmoPlugin>(memnew(VehicleWheel3DGizmoPlugin)));
add_gizmo_plugin(Ref<VisibilityNotifier3DGizmoPlugin>(memnew(VisibilityNotifier3DGizmoPlugin)));
add_gizmo_plugin(Ref<GPUParticles3DGizmoPlugin>(memnew(GPUParticles3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<GPUParticlesCollision3DGizmoPlugin>(memnew(GPUParticlesCollision3DGizmoPlugin)));
add_gizmo_plugin(Ref<CPUParticles3DGizmoPlugin>(memnew(CPUParticles3DGizmoPlugin)));
add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
add_gizmo_plugin(Ref<DecalGizmoPlugin>(memnew(DecalGizmoPlugin)));