From a548f552f43f47e9379fc58836ea21dd1d8245b1 Mon Sep 17 00:00:00 2001 From: Spencer Killen Date: Fri, 29 Sep 2023 21:57:40 -0600 Subject: [PATCH] Enable nested VoxelModifiers --- modifiers/godot/voxel_modifier_gd.cpp | 139 ++++++++++++++++++-------- modifiers/godot/voxel_modifier_gd.h | 3 + 2 files changed, 100 insertions(+), 42 deletions(-) diff --git a/modifiers/godot/voxel_modifier_gd.cpp b/modifiers/godot/voxel_modifier_gd.cpp index adb7ff843..0962cc082 100644 --- a/modifiers/godot/voxel_modifier_gd.cpp +++ b/modifiers/godot/voxel_modifier_gd.cpp @@ -6,9 +6,7 @@ namespace zylann::voxel::gd { -VoxelModifier::VoxelModifier() { - set_notify_local_transform(true); -} +VoxelModifier::VoxelModifier() { } zylann::voxel::VoxelModifier *VoxelModifier::create(zylann::voxel::VoxelModifierStack &modifiers, uint32_t id) { ZN_PRINT_ERROR("Not implemented"); @@ -71,38 +69,84 @@ float VoxelModifier::get_smoothness() const { return _smoothness; } -void VoxelModifier::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_PARENTED: { - Node *parent = get_parent(); - ZN_ASSERT_RETURN(parent != nullptr); - ZN_ASSERT_RETURN(_volume == nullptr); - VoxelLodTerrain *volume = Object::cast_to(parent); - _volume = volume; +// Not sure where the best place to put this is, probably one of the math files? +#include +bool transform3d_is_equal_approx(Transform3D a, Transform3D b) { + return Math::is_equal_approx(b.basis.rows[0].x, b.basis.rows[0].x) && + Math::is_equal_approx(a.basis.rows[0].y, b.basis.rows[0].y) && + Math::is_equal_approx(a.basis.rows[0].z, b.basis.rows[0].z) && + Math::is_equal_approx(a.basis.rows[1].x, b.basis.rows[1].x) && + Math::is_equal_approx(a.basis.rows[1].y, b.basis.rows[1].y) && + Math::is_equal_approx(a.basis.rows[1].z, b.basis.rows[1].z) && + Math::is_equal_approx(a.basis.rows[2].x, b.basis.rows[2].x) && + Math::is_equal_approx(a.basis.rows[2].y, b.basis.rows[2].y) && + Math::is_equal_approx(a.basis.rows[2].z, b.basis.rows[2].z) && + Math::is_equal_approx(a.origin.x, b.origin.x) && + Math::is_equal_approx(a.origin.y, b.origin.y) && + Math::is_equal_approx(a.origin.z, b.origin.z); +} - if (_volume != nullptr) { - VoxelData &voxel_data = _volume->get_storage(); - VoxelModifierStack &modifiers = voxel_data.get_modifiers(); - const uint32_t id = modifiers.allocate_id(); - zylann::voxel::VoxelModifier *modifier = create(modifiers, id); - - if (modifier->is_sdf()) { - zylann::voxel::VoxelModifierSdf *sdf_modifier = - static_cast(modifier); - sdf_modifier->set_operation(to_op(_operation)); - sdf_modifier->set_smoothness(_smoothness); - } +VoxelLodTerrain *VoxelModifier::find_volume() { + if (_volume != nullptr) { + return _volume; + } + Node *parent = get_parent(); + VoxelLodTerrain *volume = Object::cast_to(parent); + + if (volume != nullptr) { + mark_as_immediate_child(true); + } else { + Node3D *grandparent = get_parent_node_3d(); + volume = Object::cast_to(grandparent); + while (grandparent != nullptr && volume == nullptr) { + grandparent = grandparent->get_parent_node_3d(); + volume = Object::cast_to(grandparent); + } + if (volume) { + mark_as_immediate_child(false); + } + } + _volume = volume; - modifier->set_transform(get_transform()); - _modifier_id = id; - // TODO Optimize: on loading of a scene, this could be very bad for performance because there could be, - // a lot of modifiers on the map, but there is no distinction possible in Godot at the moment... - post_edit_modifier(*_volume, modifier->get_aabb()); - } + if (_volume == nullptr) { + return nullptr; + } + VoxelData &voxel_data = _volume->get_storage(); + VoxelModifierStack &modifiers = voxel_data.get_modifiers(); + const uint32_t id = modifiers.allocate_id(); + zylann::voxel::VoxelModifier *modifier = create(modifiers, id); + + if (modifier->is_sdf()) { + zylann::voxel::VoxelModifierSdf *sdf_modifier = + static_cast(modifier); + sdf_modifier->set_operation(to_op(_operation)); + sdf_modifier->set_smoothness(_smoothness); + } + modifier->set_transform(get_transform()); + _modifier_id = id; + // TODO Optimize: on loading of a scene, this could be very bad for performance because there could be, + // a lot of modifiers on the map, but there is no distinction possible in Godot at the moment... + post_edit_modifier(*_volume, modifier->get_aabb()); + update_configuration_warnings(); + return _volume; +} - update_configuration_warnings(); - } break; +void VoxelModifier::mark_as_immediate_child(bool v) { + _is_immediate_child = v; + if (_is_immediate_child) { + set_notify_local_transform(true); + set_notify_transform(false); + } else { + set_notify_local_transform(false); + set_notify_transform(true); + } +} +void VoxelModifier::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_PARENTED: { + find_volume(); + } break; case NOTIFICATION_UNPARENTED: { if (_volume != nullptr) { VoxelData &voxel_data = _volume->get_storage(); @@ -115,24 +159,35 @@ void VoxelModifier::_notification(int p_what) { _modifier_id = 0; } } break; - + case NOTIFICATION_POST_ENTER_TREE: { + find_volume(); + } break; + case Node3D::NOTIFICATION_TRANSFORM_CHANGED: case Node3D::NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { - if (_volume != nullptr && is_inside_tree()) { - VoxelData &voxel_data = _volume->get_storage(); + VoxelLodTerrain *volume = find_volume(); + + if (volume != nullptr && is_inside_tree()) { + VoxelData &voxel_data = volume->get_storage(); VoxelModifierStack &modifiers = voxel_data.get_modifiers(); zylann::voxel::VoxelModifier *modifier = modifiers.get_modifier(_modifier_id); ZN_ASSERT_RETURN(modifier != nullptr); const AABB prev_aabb = modifier->get_aabb(); - modifier->set_transform(get_transform()); - const AABB aabb = modifier->get_aabb(); - post_edit_modifier(*_volume, prev_aabb); - post_edit_modifier(*_volume, aabb); - // TODO Handle nesting properly, though it's a pain in the ass - // When the terrain is moved, the local transform of modifiers technically changes too. - // However it did not change relative to the terrain. But because we don't have a way to check that, - // all modifiers will trigger updates at the same time... + if (_is_immediate_child) { + modifier->set_transform(get_transform()); + } else { + Transform3D terrain_local = volume->get_global_transform().affine_inverse() * get_global_transform(); + // If the terrain moved, the modifiers do not need to be updated. + if (transform3d_is_equal_approx(terrain_local, modifier->get_transform())) { + break; + } + modifier->set_transform(terrain_local); + } + WARN_PRINT("MODIFIER WAS UPDATED"); + const AABB aabb = modifier->get_aabb(); + post_edit_modifier(*volume, prev_aabb); + post_edit_modifier(*volume, aabb); } } break; } diff --git a/modifiers/godot/voxel_modifier_gd.h b/modifiers/godot/voxel_modifier_gd.h index 1af2a6370..f20307ada 100644 --- a/modifiers/godot/voxel_modifier_gd.h +++ b/modifiers/godot/voxel_modifier_gd.h @@ -44,9 +44,12 @@ class VoxelModifier : public Node3D { private: static void _bind_methods(); + VoxelLodTerrain *find_volume(); Operation _operation = OPERATION_ADD; float _smoothness = 0.f; + bool _is_immediate_child = false; + void mark_as_immediate_child(bool v); }; // Helpers