#include "terrain_primitive.h" #include "core/error/error_list.h" #include "core/math/expression.h" #include "core/math/math_funcs.h" #include "terrain_editor/macros.h" void TerrainPrimitive::_bind_methods() { BIND_HPROPERTY(Variant::INT, blend_mode, PROPERTY_HINT_ENUM, BlendMode_hint()); BIND_PROPERTY(Variant::FLOAT, blend_range); BIND_HPROPERTY(Variant::STRING, expression, PROPERTY_HINT_EXPRESSION); ClassDB::bind_method(D_METHOD("get_expression_error"), &self_type::get_expression_error); } // by default does not modify height void TerrainPrimitive::evaluate(Vector2, float &) const {} float TerrainPrimitive::blend(float under, float over) const { float const difference{ under - over }; float const distance{ Math::abs(difference) }; // .25 because we need half of each half of the blend range to be used float const center_distance{ this->blend_range == 0.f ? 0.f : this->blend_range * 0.25f - distance / this->blend_range }; if (center_distance < 0.f) { if (this->blend_mode == Both) { return over; } else if (this->blend_mode == Peak) { return under > over ? under : over; } else { return under > over ? over : under; } } float const smooth_center_distance{ center_distance * center_distance }; if (this->blend_mode == Both) { return over + smooth_center_distance; } else { return (this->blend_mode == Peak ? (under > over ? under : over) + smooth_center_distance : (under > over ? over : under) - smooth_center_distance); } } void TerrainPrimitive::set_blend_mode(BlendMode mode) { this->blend_mode = mode; emit_changed(); } TerrainPrimitive::BlendMode TerrainPrimitive::get_blend_mode() const { return this->blend_mode; } void TerrainPrimitive::set_blend_range(float value) { this->blend_range = value; emit_changed(); } float TerrainPrimitive::get_blend_range() const { return this->blend_range; } void TerrainPrimitive::set_expression(String expression) { this->expr_text = expression; if (expression.is_empty()) { this->expression_valid = false; } else { _parse_new_expression(expression); } emit_changed(); } String TerrainPrimitive::get_expression() const { return this->expr_text; } String TerrainPrimitive::get_expression_error() const { if (this->expression_valid) { return "Valid Expression"; } else { return this->expression->get_error_text(); } } void PlanePrimitive::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, baseline); } void PlanePrimitive::_parse_new_expression(String expression) { this->expression_valid = this->expression->parse(expression, { "previous_height", "at", "baseline" }) == OK; } void PlanePrimitive::evaluate(Vector2 at, float &io_height) const { float height{ this->baseline }; if (this->expression_valid) { height = this->expression->execute({ io_height, at, this->baseline }, nullptr, false, true); } io_height = blend(io_height, height); } void PlanePrimitive::set_baseline(float value) { this->baseline = value; emit_changed(); } float PlanePrimitive::get_baseline() const { return this->baseline; } void PointPrimitive::_bind_methods() { BIND_PROPERTY(Variant::VECTOR2, center); BIND_PROPERTY(Variant::FLOAT, slope); BIND_PROPERTY(Variant::FLOAT, height); } void PointPrimitive::_parse_new_expression(String expression) { this->expression_valid = this->expression->parse(expression, { "previous_height", "at", "center", "height", "sloped_height", "distance", "slope" }) == OK; } void PointPrimitive::evaluate(Vector2 at, float &io_height) const { float distance{ at.distance_to(this->center) }; float height{ this->height + distance * this->slope }; if (this->expression_valid) { height = this->expression->execute({ io_height, at, this->center, this->height, height, distance, this->slope }, nullptr, false, true); } io_height = blend(io_height, height); } void PointPrimitive::set_center(Vector2 center) { this->center = center; emit_changed(); } Vector2 PointPrimitive::get_center() const { return this->center; } void PointPrimitive::set_slope(float radius) { this->slope = radius; emit_changed(); } float PointPrimitive::get_slope() const { return this->slope; } void PointPrimitive::set_height(float height) { this->height = height; emit_changed(); } float PointPrimitive::get_height() const { return this->height; } void NoisePrimitive::_bind_methods() { BIND_HPROPERTY(Variant::OBJECT, noise, PROPERTY_HINT_RESOURCE_TYPE, "Noise"); BIND_PROPERTY(Variant::FLOAT, noise_scale); BIND_PROPERTY(Variant::FLOAT, noise_amplitude); } void NoisePrimitive::_parse_new_expression(String expression) { this->expression_valid = this->expression->parse(expression, { "previous_height", "at", "noise", "amplitude", "scale" }) == OK; } void NoisePrimitive::evaluate(Vector2 at, float &io_height) const { if (this->noise.is_valid()) { if (Math::is_nan(io_height) || Math::is_inf(io_height)) { io_height = 0; } float noise_sample{ this->noise->get_noise_2dv(at / this->noise_scale) }; noise_sample *= this->noise_amplitude; float height{ noise_sample + io_height }; if (this->expression_valid) { height = this->expression->execute({ io_height, at, noise_sample, this->noise_amplitude, this->noise_scale }, nullptr, false, true); } io_height = blend(io_height, height); } } void NoisePrimitive::set_noise(Ref noise) { if (this->noise.is_valid()) { this->noise->disconnect_changed(callable_mp(cast_to(this), &Resource::emit_changed)); } this->noise = noise; if (this->noise.is_valid()) { this->noise->connect_changed(callable_mp(cast_to(this), &Resource::emit_changed)); } emit_changed(); } Ref NoisePrimitive::get_noise() const { return this->noise; } void NoisePrimitive::set_noise_scale(float value) { this->noise_scale = value; emit_changed(); } float NoisePrimitive::get_noise_scale() const { return this->noise_scale; } void NoisePrimitive::set_noise_amplitude(float value) { this->noise_amplitude = value; emit_changed(); } float NoisePrimitive::get_noise_amplitude() const { return this->noise_amplitude; }