121 lines
3.6 KiB
C++
121 lines
3.6 KiB
C++
#include "terrain_modifier_composite.h"
|
|
#include "core/math/math_funcs.h"
|
|
#include "terrain/terrain.h"
|
|
#include <cmath>
|
|
|
|
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<TerrainModifier>(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;
|
|
}
|
|
}
|
|
}
|