344 lines
11 KiB
C++
344 lines
11 KiB
C++
#include "noise_analysis_window.h"
|
|
#include "../../util/godot/funcs.h"
|
|
#include "chart_view.h"
|
|
|
|
#include <editor/editor_scale.h>
|
|
#include <scene/gui/box_container.h>
|
|
#include <scene/gui/grid_container.h>
|
|
#include <scene/gui/option_button.h>
|
|
#include <scene/gui/progress_bar.h>
|
|
#include <scene/gui/spin_box.h>
|
|
|
|
namespace zylann {
|
|
|
|
NoiseAnalysisWindow::NoiseAnalysisWindow() {
|
|
set_title(TTR("Noise Analysis"));
|
|
set_min_size(Vector2(300.f * EDSCALE, 0));
|
|
|
|
VBoxContainer *vbox_container = memnew(VBoxContainer);
|
|
|
|
{
|
|
GridContainer *gc1 = memnew(GridContainer);
|
|
gc1->set_columns(2);
|
|
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Dimension"));
|
|
gc1->add_child(label);
|
|
|
|
_dimension_option_button = memnew(OptionButton);
|
|
_dimension_option_button->get_popup()->add_item(TTR("2D"), DIMENSION_2D);
|
|
_dimension_option_button->get_popup()->add_item(TTR("3D"), DIMENSION_3D);
|
|
_dimension_option_button->select(0);
|
|
gc1->add_child(_dimension_option_button);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Step count"));
|
|
gc1->add_child(label);
|
|
|
|
_step_count_spinbox = memnew(SpinBox);
|
|
_step_count_spinbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
_step_count_spinbox->set_min(1);
|
|
_step_count_spinbox->set_max(100);
|
|
_step_count_spinbox->set_step(1.0);
|
|
_step_count_spinbox->set_value(10);
|
|
gc1->add_child(_step_count_spinbox);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Step minimum length"));
|
|
gc1->add_child(label);
|
|
|
|
_step_minimum_length_spinbox = memnew(SpinBox);
|
|
_step_minimum_length_spinbox->set_min(0.001);
|
|
_step_minimum_length_spinbox->set_max(1.0);
|
|
_step_minimum_length_spinbox->set_step(0.001);
|
|
_step_minimum_length_spinbox->set_value(0.01);
|
|
gc1->add_child(_step_minimum_length_spinbox);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Step maximum length"));
|
|
gc1->add_child(label);
|
|
|
|
_step_maximum_length_spinbox = memnew(SpinBox);
|
|
_step_maximum_length_spinbox->set_min(0.001);
|
|
_step_maximum_length_spinbox->set_max(1.0);
|
|
_step_maximum_length_spinbox->set_step(0.001);
|
|
_step_maximum_length_spinbox->set_value(0.1);
|
|
gc1->add_child(_step_maximum_length_spinbox);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Area size"));
|
|
gc1->add_child(label);
|
|
|
|
_area_size_spinbox = memnew(SpinBox);
|
|
_area_size_spinbox->set_min(0.0);
|
|
_area_size_spinbox->set_max(100000.0);
|
|
_area_size_spinbox->set_step(1.0);
|
|
_area_size_spinbox->set_value(4000);
|
|
gc1->add_child(_area_size_spinbox);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Samples count"));
|
|
gc1->add_child(label);
|
|
|
|
_samples_count_spinbox = memnew(SpinBox);
|
|
_samples_count_spinbox->set_min(1);
|
|
_samples_count_spinbox->set_max(100000);
|
|
_samples_count_spinbox->set_step(1);
|
|
_samples_count_spinbox->set_value(10000);
|
|
gc1->add_child(_samples_count_spinbox);
|
|
}
|
|
vbox_container->add_child(gc1);
|
|
}
|
|
{
|
|
_calculate_button = memnew(Button);
|
|
_calculate_button->set_text(TTR("Calculate"));
|
|
_calculate_button->connect("pressed", callable_mp(this, &NoiseAnalysisWindow::_on_calculate_button_pressed));
|
|
vbox_container->add_child(_calculate_button);
|
|
}
|
|
|
|
_progress_bar = memnew(ProgressBar);
|
|
_progress_bar->set_value(0);
|
|
vbox_container->add_child(_progress_bar);
|
|
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Maximum derivative over step length*:"));
|
|
label->set_tooltip_text(
|
|
TTR("Depending on the noise type, this measure can vary due to very small discontinuities, "
|
|
"so it may be interesting to try multiple step lengths, from shortest to longest."));
|
|
label->set_mouse_filter(Control::MOUSE_FILTER_STOP);
|
|
vbox_container->add_child(label);
|
|
}
|
|
|
|
_chart_view = memnew(ChartView);
|
|
_chart_view->set_custom_minimum_size(Vector2(0, 150.0 * EDSCALE));
|
|
_chart_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
|
vbox_container->add_child(_chart_view);
|
|
|
|
{
|
|
GridContainer *gc2 = memnew(GridContainer);
|
|
gc2->set_columns(2);
|
|
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Minimum value"));
|
|
gc2->add_child(label);
|
|
|
|
_minimum_value_line_edit = memnew(LineEdit);
|
|
_minimum_value_line_edit->set_editable(false);
|
|
_minimum_value_line_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
|
gc2->add_child(_minimum_value_line_edit);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Maximum value"));
|
|
gc2->add_child(label);
|
|
|
|
_maximum_value_line_edit = memnew(LineEdit);
|
|
_maximum_value_line_edit->set_editable(false);
|
|
gc2->add_child(_maximum_value_line_edit);
|
|
}
|
|
{
|
|
Label *label = memnew(Label);
|
|
label->set_text(TTR("Maximum derivative"));
|
|
gc2->add_child(label);
|
|
|
|
_maximum_derivative_line_edit = memnew(LineEdit);
|
|
_maximum_derivative_line_edit->set_editable(false);
|
|
gc2->add_child(_maximum_derivative_line_edit);
|
|
}
|
|
|
|
vbox_container->add_child(gc2);
|
|
}
|
|
|
|
add_child(vbox_container);
|
|
}
|
|
|
|
void NoiseAnalysisWindow::set_noise(Ref<FastNoise2> noise) {
|
|
_noise = noise;
|
|
}
|
|
|
|
namespace {
|
|
static thread_local std::vector<Vector2> g_precomputed_unit_vectors_2d;
|
|
static thread_local std::vector<Vector3> g_precomputed_unit_vectors_3d;
|
|
} //namespace
|
|
|
|
void NoiseAnalysisWindow::_on_calculate_button_pressed() {
|
|
ERR_FAIL_COND(_noise.is_null());
|
|
|
|
_analysis_params.dimension = Dimension(_dimension_option_button->get_selected_id());
|
|
ERR_FAIL_INDEX(_analysis_params.dimension, _DIMENSION_COUNT);
|
|
_analysis_params.area_size = _area_size_spinbox->get_value();
|
|
_analysis_params.samples_count = int(_samples_count_spinbox->get_value());
|
|
_analysis_params.step_count = int(_step_count_spinbox->get_value());
|
|
_analysis_params.step_minimum_length = _step_minimum_length_spinbox->get_value();
|
|
_analysis_params.step_maximum_length = _step_maximum_length_spinbox->get_value();
|
|
|
|
ERR_FAIL_COND(_analysis_params.step_count <= 0);
|
|
|
|
// Clear previous results
|
|
_results.maximum_derivative_per_step_length.clear();
|
|
_results.maximum_derivative_per_step_length.resize(_analysis_params.step_count);
|
|
_results.maximum_derivative = 0.f;
|
|
if (_analysis_params.dimension == DIMENSION_2D) {
|
|
_results.minimum_value = _noise->get_noise_2d_single(Vector2());
|
|
} else {
|
|
_results.minimum_value = _noise->get_noise_3d_single(Vector3());
|
|
}
|
|
_results.maximum_value = _results.minimum_value;
|
|
|
|
_current_step = 0;
|
|
|
|
// Precompute unit vectors
|
|
const int precomputed_vectors_count = 256;
|
|
if (_analysis_params.dimension == DIMENSION_2D) {
|
|
g_precomputed_unit_vectors_2d.resize(precomputed_vectors_count);
|
|
for (int i = 0; i < precomputed_vectors_count; ++i) {
|
|
const float a = Math_TAU * float(i) / float(precomputed_vectors_count);
|
|
g_precomputed_unit_vectors_2d[i] = Vector2(Math::cos(a), Math::sin(a));
|
|
}
|
|
} else {
|
|
g_precomputed_unit_vectors_3d.resize(precomputed_vectors_count);
|
|
for (int i = 0; i < precomputed_vectors_count; ++i) {
|
|
// TODO Uniform repartition of 3D vectors?
|
|
g_precomputed_unit_vectors_3d[i] =
|
|
Vector3(Math::random(-1.f, 1.f), Math::random(-1.f, 1.f), Math::random(-1.f, 1.f)).normalized();
|
|
}
|
|
}
|
|
|
|
_progress_bar->set_value(0);
|
|
|
|
_calculate_button->set_disabled(true);
|
|
|
|
set_process(true);
|
|
}
|
|
|
|
void NoiseAnalysisWindow::_notification(int p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_PROCESS:
|
|
_process();
|
|
break;
|
|
|
|
case NOTIFICATION_VISIBILITY_CHANGED:
|
|
if (!is_visible()) {
|
|
// Release reference when the window is closed
|
|
set_noise(Ref<FastNoise2>());
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NoiseAnalysisWindow::_process() {
|
|
ERR_FAIL_COND(_noise.is_null());
|
|
ERR_FAIL_COND(_analysis_params.step_count <= 0);
|
|
|
|
if (!is_processing()) {
|
|
return;
|
|
}
|
|
|
|
const float step_length = Math::lerp(_analysis_params.step_minimum_length, _analysis_params.step_maximum_length,
|
|
float(_current_step) / _analysis_params.step_count);
|
|
|
|
std::vector<float> x_cache;
|
|
std::vector<float> y_cache;
|
|
std::vector<float> z_cache;
|
|
std::vector<float> noise_cache;
|
|
std::vector<float> noise_cache2;
|
|
|
|
x_cache.clear();
|
|
y_cache.clear();
|
|
z_cache.clear();
|
|
|
|
x_cache.resize(_analysis_params.samples_count);
|
|
y_cache.resize(_analysis_params.samples_count);
|
|
z_cache.resize(_analysis_params.samples_count);
|
|
|
|
noise_cache.resize(_analysis_params.samples_count);
|
|
noise_cache2.resize(_analysis_params.samples_count);
|
|
|
|
if (_analysis_params.dimension == DIMENSION_2D) {
|
|
// 2D
|
|
for (int i = 0; i < _analysis_params.samples_count; ++i) {
|
|
const float x = Math::random(-_analysis_params.area_size, _analysis_params.area_size);
|
|
const float y = Math::random(-_analysis_params.area_size, _analysis_params.area_size);
|
|
x_cache[i] = x;
|
|
y_cache[i] = y;
|
|
}
|
|
|
|
_noise->get_noise_2d_series(to_span_const(x_cache), to_span_const(y_cache), to_span(noise_cache));
|
|
|
|
for (size_t i = 0; i < noise_cache.size(); ++i) {
|
|
const Vector2 u = step_length * g_precomputed_unit_vectors_2d[i % g_precomputed_unit_vectors_2d.size()];
|
|
x_cache[i] += u.x;
|
|
y_cache[i] += u.y;
|
|
}
|
|
|
|
_noise->get_noise_2d_series(to_span_const(x_cache), to_span_const(y_cache), to_span(noise_cache2));
|
|
|
|
} else {
|
|
// 3D
|
|
for (int i = 0; i < _analysis_params.samples_count; ++i) {
|
|
const float x = Math::random(-_analysis_params.area_size, _analysis_params.area_size);
|
|
const float y = Math::random(-_analysis_params.area_size, _analysis_params.area_size);
|
|
const float z = Math::random(-_analysis_params.area_size, _analysis_params.area_size);
|
|
x_cache[i] = x;
|
|
y_cache[i] = y;
|
|
z_cache[i] = z;
|
|
}
|
|
|
|
_noise->get_noise_3d_series(
|
|
to_span_const(x_cache), to_span_const(y_cache), to_span_const(z_cache), to_span(noise_cache));
|
|
|
|
for (size_t i = 0; i < noise_cache.size(); ++i) {
|
|
const Vector3 u = step_length * g_precomputed_unit_vectors_3d[i % g_precomputed_unit_vectors_3d.size()];
|
|
x_cache[i] += u.x;
|
|
y_cache[i] += u.y;
|
|
z_cache[i] += u.z;
|
|
}
|
|
|
|
_noise->get_noise_3d_series(
|
|
to_span_const(x_cache), to_span_const(y_cache), to_span_const(z_cache), to_span(noise_cache2));
|
|
}
|
|
|
|
float max_derivative = 0.f;
|
|
for (size_t i = 0; i < noise_cache.size(); ++i) {
|
|
_results.minimum_value = math::min(_results.minimum_value, noise_cache[i]);
|
|
_results.maximum_value = math::max(_results.maximum_value, noise_cache[i]);
|
|
const float derivative = Math::abs((noise_cache2[i] - noise_cache[i]) / step_length);
|
|
max_derivative = math::max(derivative, max_derivative);
|
|
}
|
|
|
|
_results.maximum_derivative = math::max(max_derivative, _results.maximum_derivative);
|
|
_results.maximum_derivative_per_step_length.write[_current_step] = Vector2(step_length, max_derivative);
|
|
|
|
++_current_step;
|
|
|
|
_progress_bar->set_value(100.f * float(_current_step) / float(_analysis_params.step_count));
|
|
|
|
if (_current_step >= _analysis_params.step_count) {
|
|
set_process(false);
|
|
|
|
_chart_view->set_points(to_span(_results.maximum_derivative_per_step_length));
|
|
_chart_view->auto_fit_view(Vector2(0.1, 0.1));
|
|
|
|
_minimum_value_line_edit->set_text(String::num_real(_results.minimum_value));
|
|
_maximum_value_line_edit->set_text(String::num_real(_results.maximum_value));
|
|
_maximum_derivative_line_edit->set_text(String::num_real(_results.maximum_derivative));
|
|
|
|
_calculate_button->set_disabled(false);
|
|
}
|
|
}
|
|
|
|
void NoiseAnalysisWindow::_bind_methods() {}
|
|
|
|
} // namespace zylann
|