#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); } // 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 PlanePrimitive::_bind_methods() { BIND_PROPERTY(Variant::FLOAT, baseline); } void PlanePrimitive::evaluate(Vector2, float &io_height) const { io_height = blend(io_height, this->baseline); } 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::evaluate(Vector2 at, float &io_height) const { float distance{ at.distance_to(this->center) }; io_height = blend(io_height, this->height + distance * this->slope); } 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::evaluate(Vector2 at, float &io_height) const { if (this->noise.is_valid()) { float noise_sample{ this->noise->get_noise_2dv(at / this->noise_scale) }; noise_sample *= this->noise_amplitude; io_height = blend(io_height, io_height + noise_sample); } } void NoisePrimitive::set_noise(Ref noise) { this->noise = noise; 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; } void ExpressionPrimitive::_bind_methods() { BIND_HPROPERTY(Variant::STRING, expression, PROPERTY_HINT_EXPRESSION); } void ExpressionPrimitive::evaluate(Vector2 at, float &io_height) const { if (!this->valid) { return; } Variant result{ this->expression->execute({ io_height, at }, nullptr, false, true) }; if (!this->expression->has_execute_failed()) { io_height = blend(io_height, float(result.get(0))); } } void ExpressionPrimitive::set_expression(String expression) { this->expression_string = expression; this->expression.unref(); this->expression = memnew(Expression); Error error{ this->expression->parse(this->expression_string, { "height", "at" }) }; if ((this->valid = error == OK)) { emit_changed(); } } String ExpressionPrimitive::get_expression() const { return this->expression_string; }