Skip to content

Commit

Permalink
Enable nested VoxelModifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
sjkillen committed Sep 30, 2023
1 parent 02596c1 commit 54db024
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 49 deletions.
157 changes: 108 additions & 49 deletions modifiers/godot/voxel_modifier_gd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -71,38 +69,112 @@ 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<VoxelLodTerrain>(parent);
_volume = volume;
// Not sure where the best place to put this is, probably one of the math files?
#include <core/math/math_funcs.h>
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<zylann::voxel::VoxelModifierSdf *>(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());
VoxelLodTerrain *VoxelModifier::find_volume() {
if (_volume != nullptr) {
return _volume;
}
Node *parent = get_parent();
VoxelLodTerrain *volume = Object::cast_to<VoxelLodTerrain>(parent);

if (volume != nullptr) {
mark_as_immediate_child(true);
} else {
Node3D *grandparent = get_parent_node_3d();
volume = Object::cast_to<VoxelLodTerrain>(grandparent);
while (grandparent != nullptr && volume == nullptr) {
grandparent = grandparent->get_parent_node_3d();
volume = Object::cast_to<VoxelLodTerrain>(grandparent);
}
if (volume) {
mark_as_immediate_child(false);
}
}
_volume = volume;

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<zylann::voxel::VoxelModifierSdf *>(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;
}

void VoxelModifier::update_volume() {
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();

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())) {
return;
}
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);
}
}

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();
Expand All @@ -115,25 +187,12 @@ void VoxelModifier::_notification(int p_what) {
_modifier_id = 0;
}
} break;

case NOTIFICATION_POST_ENTER_TREE: {
update_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();
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...
}
update_volume();
} break;
}
}
Expand Down
3 changes: 3 additions & 0 deletions modifiers/godot/voxel_modifier_gd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 54db024

Please sign in to comment.