terrain-editor/modules/terrain_editor/terrain_primitive.cpp

189 lines
5 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);
}
// 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()) {
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;
io_height = blend(io_height, io_height + noise_sample);
}
}
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;
}
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;
}