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

[WIP] Enable nested VoxelModifiers #558

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
161 changes: 111 additions & 50 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,111 @@ 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);
}
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 +186,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 All @@ -156,9 +214,12 @@ PackedStringArray VoxelModifier::_get_configuration_warnings() const {

void VoxelModifier::get_configuration_warnings(PackedStringArray &warnings) const {
if (_volume == nullptr) {
warnings.append(ZN_TTR("The parent of this node must be of type {0}.")
warnings.append(ZN_TTR("This node must be a child of a node of type {0}.")
.format(varray(VoxelLodTerrain::get_class_static())));
}
if (!_is_immediate_child) {
warnings.append(ZN_TTR("Avoid nested voxel modifiers when possible"));
}
}

#endif
Expand Down
4 changes: 4 additions & 0 deletions modifiers/godot/voxel_modifier_gd.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ 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);
void update_volume();
};

// Helpers
Expand Down
1 change: 1 addition & 0 deletions modifiers/godot/voxel_modifier_mesh_gd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ void VoxelModifierMesh::_on_mesh_sdf_baked() {
#ifdef TOOLS_ENABLED

void VoxelModifierMesh::get_configuration_warnings(PackedStringArray &warnings) const {
VoxelModifier::get_configuration_warnings(warnings);
if (_mesh_sdf.is_null()) {
warnings.append(
ZN_TTR("A {0} resource is required for {1} to function.")
Expand Down