Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support custom AABBs within MultiMesh resources #79833

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/classes/CPUParticles3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@
<member name="tangential_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0">
Minimum tangent acceleration.
</member>
<member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb" default="AABB(-4, -4, -4, 8, 8, 8)">
The [AABB] that determines the node's region which needs to be visible on screen for the particle system to be active.
Grow the box if particles suddenly appear/disappear when the node enters/exits the screen. The [AABB] can be grown via code or with the [b]Particles → Generate AABB[/b] editor tool.
</member>
</members>
<signals>
<signal name="finished">
Expand Down
4 changes: 4 additions & 0 deletions doc/classes/MultiMesh.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@
</member>
<member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Use [method set_instance_color] instead.">
</member>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
Custom AABB for this MultiMesh resource. Setting this manually prevents costly runtime AABB recalculations.
</member>
<member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Use [method set_instance_custom_data] instead.">
See [method set_instance_custom_data].
</member>
<member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0">
Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect.
Expand Down
15 changes: 15 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2332,6 +2332,13 @@
[b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible.
</description>
</method>
<method name="multimesh_get_custom_aabb" qualifiers="const">
<return type="AABB" />
<param index="0" name="multimesh" type="RID" />
<description>
Returns the custom AABB defined for this MultiMesh resource.
</description>
</method>
<method name="multimesh_get_instance_count" qualifiers="const">
<return type="int" />
<param index="0" name="multimesh" type="RID" />
Expand Down Expand Up @@ -2442,6 +2449,14 @@
[/codeblock]
</description>
</method>
<method name="multimesh_set_custom_aabb">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
<param index="1" name="aabb" type="AABB" />
<description>
Sets the custom AABB for this MultiMesh resource.
</description>
</method>
<method name="multimesh_set_mesh">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
Expand Down
31 changes: 27 additions & 4 deletions drivers/gles3/storage/mesh_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,9 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b

void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) {
ERR_FAIL_COND(multimesh->mesh.is_null());
if (multimesh->custom_aabb != AABB()) {
return;
}
AABB aabb;
AABB mesh_aabb = mesh_get_aabb(multimesh->mesh);
for (int i = 0; i < p_instances; i++) {
Expand Down Expand Up @@ -1749,9 +1752,25 @@ RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
return multimesh->mesh;
}

void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL(multimesh);
multimesh->custom_aabb = p_aabb;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}

AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
return multimesh->custom_aabb;
}

AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
if (multimesh->custom_aabb != AABB()) {
return multimesh->custom_aabb;
}
if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
}
Expand Down Expand Up @@ -1943,8 +1962,10 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr();

_multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
if (multimesh->custom_aabb != AABB()) {
_multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
}
}

Expand Down Expand Up @@ -2091,9 +2112,11 @@ void MeshStorage::_update_dirty_multimeshes() {
}

if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->aabb_dirty = false;
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
if (multimesh->custom_aabb != AABB()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/storage/mesh_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ struct MultiMesh {
bool uses_custom_data = false;
int visible_instances = -1;
AABB aabb;
AABB custom_aabb;
bool aabb_dirty = false;
bool buffer_set = false;
uint32_t stride_cache = 0;
Expand Down Expand Up @@ -505,6 +506,8 @@ class MeshStorage : public RendererMeshStorage {
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;

virtual RID multimesh_get_mesh(RID p_multimesh) const override;
virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB multimesh_get_aabb(RID p_multimesh) const override;

virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
Expand Down
68 changes: 68 additions & 0 deletions editor/plugins/cpu_particles_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,60 @@ void CPUParticles3DEditor::_menu_option(int p_option) {
ur->commit_action(false);

} break;
case MENU_OPTION_GENERATE_AABB: {
// Add one second to the default generation lifetime, since the progress is updated every second.
generate_seconds->set_value(MAX(1.0, trunc(node->get_lifetime()) + 1.0));

if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
// Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
generate_aabb->popup_centered();
} else {
// Generate the visibility AABB immediately.
_generate_aabb();
}
} break;
}
}

void CPUParticles3DEditor::_generate_aabb() {
double time = generate_seconds->get_value();

double running = 0.0;

EditorProgress ep("gen_aabb", TTR("Generating Visibility AABB (Waiting for Particle Simulation)"), int(time));

bool was_emitting = node->is_emitting();
if (!was_emitting) {
node->set_emitting(true);
OS::get_singleton()->delay_usec(1000);
}

AABB rect;

while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
ep.step("Generating...", int(running), true);
OS::get_singleton()->delay_usec(1000);

AABB capture = node->capture_aabb();
if (rect == AABB()) {
rect = capture;
} else {
rect.merge_with(capture);
}

running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
}

if (!was_emitting) {
node->set_emitting(false);
}

EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Generate Visibility AABB"));
ur->add_do_method(node, "set_visibility_aabb", rect);
ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());
ur->commit_action();
}

void CPUParticles3DEditor::edit(CPUParticles3D *p_particles) {
Expand Down Expand Up @@ -117,9 +170,24 @@ CPUParticles3DEditor::CPUParticles3DEditor() {

options->set_text(TTR("CPUParticles3D"));
options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART);
options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES);
options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles3DEditor::_menu_option));

generate_aabb = memnew(ConfirmationDialog);
generate_aabb->set_title(TTR("Generate Visibility AABB"));
VBoxContainer *genvb = memnew(VBoxContainer);
generate_aabb->add_child(genvb);
generate_seconds = memnew(SpinBox);
genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
generate_seconds->set_min(0.1);
generate_seconds->set_max(25);
generate_seconds->set_value(2);

add_child(generate_aabb);

generate_aabb->connect("confirmed", callable_mp(this, &CPUParticles3DEditor::_generate_aabb));
}

void CPUParticles3DEditorPlugin::edit(Object *p_object) {
Expand Down
5 changes: 5 additions & 0 deletions editor/plugins/cpu_particles_3d_editor_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,19 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase {
GDCLASS(CPUParticles3DEditor, GPUParticles3DEditorBase);

enum Menu {
MENU_OPTION_GENERATE_AABB,
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
MENU_OPTION_CLEAR_EMISSION_VOLUME,
MENU_OPTION_RESTART,
MENU_OPTION_CONVERT_TO_GPU_PARTICLES,
};

ConfirmationDialog *generate_aabb = nullptr;
SpinBox *generate_seconds = nullptr;
CPUParticles3D *node = nullptr;

void _generate_aabb();

void _menu_option(int);

friend class CPUParticles3DEditorPlugin;
Expand Down
47 changes: 47 additions & 0 deletions editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@
#include "cpu_particles_3d_gizmo_plugin.h"

#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/cpu_particles_3d.h"

CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
create_material("particles_material", gizmo_color);
gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
create_material("particles_solid_material", gizmo_color);
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCPUParticles3D"), EditorStringName(EditorIcons)));
}

Expand All @@ -56,6 +61,48 @@ bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
}

void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d());

p_gizmo->clear();

Vector<Vector3> lines;
AABB aabb = particles->get_visibility_aabb();

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] = aabb.position[i] + aabb.size[i];
ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5;
ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5;
handles.push_back(ax);
}

Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
handles.push_back(center + ax);
lines.push_back(center);
lines.push_back(center + ax);
}

Ref<Material> material = get_material("particles_material", p_gizmo);

p_gizmo->add_lines(lines, material);

if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}

Ref<Material> icon = get_material("particles_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}
20 changes: 20 additions & 0 deletions scene/3d/cpu_particles_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ void CPUParticles3D::set_randomness_ratio(real_t p_ratio) {
randomness_ratio = p_ratio;
}

void CPUParticles3D::set_visibility_aabb(const AABB &p_aabb) {
RS::get_singleton()->multimesh_set_custom_aabb(multimesh, p_aabb);
visibility_aabb = p_aabb;
update_gizmos();
}

void CPUParticles3D::set_lifetime_randomness(double p_random) {
lifetime_randomness = p_random;
}
Expand Down Expand Up @@ -141,6 +147,10 @@ real_t CPUParticles3D::get_randomness_ratio() const {
return randomness_ratio;
}

AABB CPUParticles3D::get_visibility_aabb() const {
return visibility_aabb;
}

double CPUParticles3D::get_lifetime_randomness() const {
return lifetime_randomness;
}
Expand Down Expand Up @@ -520,6 +530,11 @@ bool CPUParticles3D::get_split_scale() {
return split_scale;
}

AABB CPUParticles3D::capture_aabb() const {
RS::get_singleton()->multimesh_set_custom_aabb(multimesh, AABB());
return RS::get_singleton()->multimesh_get_aabb(multimesh);
}

void CPUParticles3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
p_property.usage = PROPERTY_USAGE_NONE;
Expand Down Expand Up @@ -1341,6 +1356,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) {
set_pre_process_time(gpu_particles->get_pre_process_time());
set_explosiveness_ratio(gpu_particles->get_explosiveness_ratio());
set_randomness_ratio(gpu_particles->get_randomness_ratio());
set_visibility_aabb(gpu_particles->get_visibility_aabb());
set_use_local_coordinates(gpu_particles->get_use_local_coordinates());
set_fixed_fps(gpu_particles->get_fixed_fps());
set_fractional_delta(gpu_particles->get_fractional_delta());
Expand Down Expand Up @@ -1420,6 +1436,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles3D::set_pre_process_time);
ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles3D::set_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles3D::set_randomness_ratio);
ClassDB::bind_method(D_METHOD("set_visibility_aabb", "aabb"), &CPUParticles3D::set_visibility_aabb);
ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "random"), &CPUParticles3D::set_lifetime_randomness);
ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles3D::set_use_local_coordinates);
ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles3D::set_fixed_fps);
Expand All @@ -1433,6 +1450,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles3D::get_pre_process_time);
ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles3D::get_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles3D::get_randomness_ratio);
ClassDB::bind_method(D_METHOD("get_visibility_aabb"), &CPUParticles3D::get_visibility_aabb);
ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &CPUParticles3D::get_lifetime_randomness);
ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles3D::get_use_local_coordinates);
ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles3D::get_fixed_fps);
Expand Down Expand Up @@ -1461,6 +1479,7 @@ void CPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Drawing", "");
ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb", PROPERTY_HINT_NONE, "suffix:m"), "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::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
Expand Down Expand Up @@ -1665,6 +1684,7 @@ CPUParticles3D::CPUParticles3D() {

set_emitting(true);
set_amount(8);
set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)));

set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
Expand Down
Loading
Loading