terrain-editor/modules/terrain_editor/terrain_primitive.cpp

208 lines
5.9 KiB
C++

#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> noise) {
if (this->noise.is_valid()) {
this->noise->disconnect_changed(callable_mp(cast_to<Resource>(this), &Resource::emit_changed));
}
this->noise = noise;
if (this->noise.is_valid()) {
this->noise->connect_changed(callable_mp(cast_to<Resource>(this), &Resource::emit_changed));
}
emit_changed();
}
Ref<Noise> 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;
}