#include "terrain_modifier_composite.h" #include "core/math/math_funcs.h" #include "terrain/terrain.h" #include void TerrainModifierComposite::_bind_methods() { BIND_HPROPERTY(Variant::INT, mode, PROPERTY_HINT_ENUM, CompositeMode_hint()); } void TerrainModifierComposite::push_all_changes() { for (TerrainModifier *mod : this->sub_modifiers) { push_changed(mod->get_bounds()); } } void TerrainModifierComposite::update_sub_modifiers() { for (TerrainModifier *mod : this->sub_modifiers) { push_changed(mod->get_bounds()); } this->sub_modifiers.clear(); for (Variant var : get_children()) { if (TerrainModifier * mod{ cast_to(var) }) { this->sub_modifiers.push_back(mod); mod->set_terrain(get_terrain()); push_changed(mod->get_bounds()); } } } void TerrainModifierComposite::terrain_changed(Terrain *terrain) { for (TerrainModifier *mod : this->sub_modifiers) { mod->set_terrain(terrain); } } void TerrainModifierComposite::_notification(int what) { switch (what) { default: return; case NOTIFICATION_ENTER_TREE: set_notify_transform(true); if (!is_ready()) { connect(sig_terrain_changed, callable_mp(this, &self_type::terrain_changed)); } return; case NOTIFICATION_CHILD_ORDER_CHANGED: if (!is_ready()) { return; } // fall through case NOTIFICATION_READY: update_sub_modifiers(); return; } } float TerrainModifierComposite::evaluate_at(Vector2 world_coordinate, float before) { if (this->sub_modifiers.is_empty()) { return before; } switch (this->mode) { case Normal: { // evaluate as if the sub-modifiers are part of the parent directly, with no modification float height{ before }; for (TerrainModifier *mod : this->sub_modifiers) { height = mod->evaluate_at(world_coordinate, height); } return height; } case Multiply: { // evaluate independently, multiplying the /change/ each modifier makes together float result_delta{ 1.f }; for (TerrainModifier *mod : this->sub_modifiers) { result_delta *= mod->evaluate_at(world_coordinate, before) - before; } return result_delta + before; } case Add: { // evaluate independently, adding together the /change/ each modifier makes float result{ 0.f }; for (TerrainModifier *mod : this->sub_modifiers) { result += mod->evaluate_at(world_coordinate, before) - before; } return before + result; } case Max: { // always select the largest change float result_delta{ 0.f }; for (TerrainModifier *mod : this->sub_modifiers) { float delta{ mod->evaluate_at(world_coordinate, before) - before }; if (Math::abs(delta) > Math::abs(result_delta)) { result_delta = delta; } } return before + result_delta; } case Min: { // always select the smallest change float result_delta{ INFINITY }; for (TerrainModifier *mod : this->sub_modifiers) { float delta{ mod->evaluate_at(world_coordinate, before) - before }; if (Math::abs(delta) < Math::abs(result_delta)) { result_delta = delta; } } return before + result_delta; } case Average: { float total_delta{ 0.f }; for (TerrainModifier *mod : this->sub_modifiers) { total_delta += mod->evaluate_at(world_coordinate, before) - before; } return before + total_delta / (float)this->sub_modifiers.size(); } case NormalMultiply: { float add_result{ 0.f }; float multiply_result{ 1.f }; for (TerrainModifier *mod : this->sub_modifiers) { float evaluated{ mod->evaluate_at(world_coordinate, before) - before }; add_result += evaluated; multiply_result *= evaluated; } return before + add_result + multiply_result; } } }