Added inspector plugin to edit VoxelMeshSDF

master
Marc Gilleron 2022-05-05 20:57:59 +01:00
parent 3289b44136
commit f993796ca1
10 changed files with 312 additions and 21 deletions

1
SCsub
View File

@ -66,6 +66,7 @@ if env["tools"]:
"editor/instancer/*.cpp",
"editor/instance_library/*.cpp",
"editor/vox/*.cpp",
"editor/mesh_sdf/*.cpp",
]
voxel_files += voxel_editor_files

View File

@ -0,0 +1,51 @@
#include "voxel_mesh_sdf_editor_plugin.h"
#include "../../edition/voxel_mesh_sdf_gd.h"
#include "voxel_mesh_sdf_viewer.h"
#include <editor/editor_scale.h>
namespace zylann::voxel {
bool VoxelMeshSDFInspectorPlugin::can_handle(Object *p_object) {
return Object::cast_to<VoxelMeshSDF>(p_object) != nullptr;
}
void VoxelMeshSDFInspectorPlugin::parse_begin(Object *p_object) {
VoxelMeshSDFViewer *viewer = memnew(VoxelMeshSDFViewer);
add_custom_control(viewer);
VoxelMeshSDF *mesh_sdf = Object::cast_to<VoxelMeshSDF>(p_object);
ERR_FAIL_COND(mesh_sdf == nullptr);
viewer->set_mesh_sdf(mesh_sdf);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VoxelMeshSDFEditorPlugin::VoxelMeshSDFEditorPlugin() {}
bool VoxelMeshSDFEditorPlugin::handles(Object *p_object) const {
ERR_FAIL_COND_V(p_object == nullptr, false);
return Object::cast_to<VoxelMeshSDF>(p_object) != nullptr;
}
void VoxelMeshSDFEditorPlugin::edit(Object *p_object) {
//_mesh_sdf = p_object;
}
void VoxelMeshSDFEditorPlugin::make_visible(bool visible) {
//_mesh_sdf.unref();
}
void VoxelMeshSDFEditorPlugin::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
_inspector_plugin.instantiate();
// TODO Why can other Godot plugins do this in the constructor??
// I found I could not put this in the constructor,
// otherwise `add_inspector_plugin` causes ANOTHER editor plugin to leak on exit... Oo
add_inspector_plugin(_inspector_plugin);
} else if (p_what == NOTIFICATION_EXIT_TREE) {
remove_inspector_plugin(_inspector_plugin);
}
}
} // namespace zylann::voxel

View File

@ -0,0 +1,32 @@
#ifndef VOXEL_MESH_SDF_EDITOR_PLUGIN_H
#define VOXEL_MESH_SDF_EDITOR_PLUGIN_H
#include <editor/editor_plugin.h>
namespace zylann::voxel {
class VoxelMeshSDFInspectorPlugin : public EditorInspectorPlugin {
GDCLASS(VoxelMeshSDFInspectorPlugin, EditorInspectorPlugin)
public:
bool can_handle(Object *p_object);
void parse_begin(Object *p_object);
};
class VoxelMeshSDFEditorPlugin : public EditorPlugin {
GDCLASS(VoxelMeshSDFEditorPlugin, EditorPlugin)
public:
VoxelMeshSDFEditorPlugin();
bool handles(Object *p_object) const override;
void edit(Object *p_object) override;
void make_visible(bool visible) override;
private:
void _notification(int p_what);
Ref<VoxelMeshSDFInspectorPlugin> _inspector_plugin;
};
} // namespace zylann::voxel
#endif // VOXEL_MESH_SDF_EDITOR_PLUGIN_H

View File

@ -0,0 +1,105 @@
#include "voxel_mesh_sdf_viewer.h"
#include <editor/editor_scale.h>
#include <scene/gui/button.h>
#include <scene/gui/label.h>
#include <scene/gui/texture_rect.h>
namespace zylann::voxel {
VoxelMeshSDFViewer::VoxelMeshSDFViewer() {
_texture_rect = memnew(TextureRect);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
_texture_rect->set_custom_minimum_size(Vector2(0, EDSCALE * 128.0));
_texture_rect->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
add_child(_texture_rect);
_info_label = memnew(Label);
_info_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
add_child(_info_label);
update_info_label();
Button *bake_button = memnew(Button);
bake_button->connect("pressed", callable_mp(this, &VoxelMeshSDFViewer::_on_bake_button_pressed));
add_child(bake_button);
_bake_button = bake_button;
update_bake_button();
}
void VoxelMeshSDFViewer::set_mesh_sdf(Ref<VoxelMeshSDF> mesh_sdf) {
if (_mesh_sdf.is_valid()) {
_mesh_sdf->disconnect("baked", callable_mp(this, &VoxelMeshSDFViewer::_on_mesh_sdf_baked));
}
if (_mesh_sdf != mesh_sdf) {
_mesh_sdf = mesh_sdf;
update_view();
}
if (_mesh_sdf.is_valid()) {
_mesh_sdf->connect("baked", callable_mp(this, &VoxelMeshSDFViewer::_on_mesh_sdf_baked));
}
update_bake_button();
update_info_label();
}
void VoxelMeshSDFViewer::update_view() {
ERR_FAIL_COND(_mesh_sdf.is_null());
if (!_mesh_sdf->is_baked()) {
_texture_rect->set_texture(Ref<Texture>());
return;
}
Ref<gd::VoxelBuffer> vb = _mesh_sdf->get_voxel_buffer();
float sdf_min;
float sdf_max;
vb->get_buffer().get_range_f(sdf_min, sdf_max, VoxelBufferInternal::CHANNEL_SDF);
Ref<Image> image = vb->debug_print_sdf_y_slice((sdf_max - sdf_min) / 2.0, vb->get_size_y() / 2);
Ref<ImageTexture> texture;
texture.instantiate();
texture->create_from_image(image);
_texture_rect->set_texture(texture);
// TODO Implement a raymarched view.
// I can't do it at the moment because Godot 4 support for post-processing shaders seems broken.
}
void VoxelMeshSDFViewer::_on_bake_button_pressed() {
ERR_FAIL_COND(_mesh_sdf.is_null());
_mesh_sdf->bake_async(get_tree());
update_bake_button();
}
void VoxelMeshSDFViewer::_on_mesh_sdf_baked() {
if (_mesh_sdf.is_valid()) {
update_view();
}
update_bake_button();
update_info_label();
}
void VoxelMeshSDFViewer::update_bake_button() {
if (_mesh_sdf.is_valid()) {
if (_mesh_sdf->is_baking()) {
_bake_button->set_text(TTR("Baking..."));
_bake_button->set_disabled(true);
} else {
_bake_button->set_text(TTR("Bake"));
_bake_button->set_disabled(false);
}
} else {
_bake_button->set_text(TTR("Bake"));
_bake_button->set_disabled(true);
}
}
void VoxelMeshSDFViewer::update_info_label() {
if (_mesh_sdf.is_valid() && _mesh_sdf->is_baked()) {
const Vector3i size = _mesh_sdf->get_voxel_buffer()->get_size();
_info_label->set_text(String("{0}x{1}x{2}").format(varray(size.x, size.y, size.z)));
} else {
_info_label->set_text("<Empty>");
}
}
} // namespace zylann::voxel

View File

@ -0,0 +1,36 @@
#ifndef VOXEL_MESH_SDF_VIEWER_H
#define VOXEL_MESH_SDF_VIEWER_H
#include "../../edition/voxel_mesh_sdf_gd.h"
#include <scene/gui/box_container.h>
class TextureRect;
class Button;
namespace zylann::voxel {
class VoxelMeshSDFViewer : public VBoxContainer {
GDCLASS(VoxelMeshSDFViewer, VBoxContainer)
public:
VoxelMeshSDFViewer();
void set_mesh_sdf(Ref<VoxelMeshSDF> mesh_sdf);
void update_view();
private:
void _on_bake_button_pressed();
void _on_mesh_sdf_baked();
void update_bake_button();
void update_info_label();
TextureRect *_texture_rect = nullptr;
Label *_info_label = nullptr;
Button *_bake_button = nullptr;
Ref<VoxelMeshSDF> _mesh_sdf;
};
} // namespace zylann::voxel
#endif // VOXEL_MESH_SDF_VIEWER_H

View File

@ -55,6 +55,7 @@
#include "editor/graph/voxel_graph_editor_plugin.h"
#include "editor/instance_library/voxel_instance_library_editor_plugin.h"
#include "editor/instancer/voxel_instancer_editor_plugin.h"
#include "editor/mesh_sdf/voxel_mesh_sdf_editor_plugin.h"
#include "editor/terrain/voxel_terrain_editor_plugin.h"
#include "editor/vox/vox_editor_plugin.h"
#include "editor/voxel_debug.h"
@ -175,6 +176,7 @@ void register_voxel_types() {
EditorPlugins::add_by_type<ZN_FastNoiseLiteEditorPlugin>();
EditorPlugins::add_by_type<magica::VoxEditorPlugin>();
EditorPlugins::add_by_type<VoxelInstancerEditorPlugin>();
EditorPlugins::add_by_type<VoxelMeshSDFEditorPlugin>();
#ifdef VOXEL_ENABLE_FAST_NOISE_2
EditorPlugins::add_by_type<FastNoise2EditorPlugin>();
#endif

View File

@ -219,6 +219,34 @@ Ref<Image> VoxelBuffer::debug_print_sdf_to_image_top_down(const VoxelBufferInter
return im;
}
Ref<Image> VoxelBuffer::debug_print_sdf_y_slice(float scale, int y) const {
const VoxelBufferInternal &buffer = *_buffer;
const Vector3i res = buffer.get_size();
ERR_FAIL_COND_V(y < 0 || y >= res.y, Ref<Image>());
Ref<Image> im;
im.instantiate();
im->create(res.x, res.z, false, Image::FORMAT_RGB8);
const Color nega_col(0.5f, 0.5f, 1.0f);
const Color posi_col(1.0f, 0.6f, 0.1f);
const Color black(0.f, 0.f, 0.f);
for (int z = 0; z < res.z; ++z) {
for (int x = 0; x < res.x; ++x) {
const float sd = scale * buffer.get_voxel_f(x, y, z, VoxelBufferInternal::CHANNEL_SDF);
const float nega = math::clamp(-sd, 0.0f, 1.0f);
const float posi = math::clamp(sd, 0.0f, 1.0f);
const Color col = math::lerp(black, nega_col, nega) + math::lerp(black, posi_col, posi);
im->set_pixel(x, z, col);
}
}
return im;
}
Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
Array images;
@ -226,27 +254,7 @@ Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
const Vector3i res = buffer.get_size();
for (int y = 0; y < res.y; ++y) {
Ref<Image> im;
im.instantiate();
im->create(res.x, res.z, false, Image::FORMAT_RGB8);
const Color nega_col(0.5f, 0.5f, 1.0f);
const Color posi_col(1.0f, 0.6f, 0.1f);
const Color black(0.f, 0.f, 0.f);
for (int z = 0; z < res.z; ++z) {
for (int x = 0; x < res.x; ++x) {
const float sd = scale * buffer.get_voxel_f(x, y, z, VoxelBufferInternal::CHANNEL_SDF);
const float nega = math::clamp(-sd, 0.0f, 1.0f);
const float posi = math::clamp(sd, 0.0f, 1.0f);
const Color col = math::lerp(black, nega_col, nega) + math::lerp(black, posi_col, posi);
im->set_pixel(x, z, col);
}
}
images.append(im);
images.append(debug_print_sdf_y_slice(scale, y));
}
return images;

View File

@ -168,6 +168,7 @@ public:
Ref<Image> debug_print_sdf_to_image_top_down();
static Ref<Image> debug_print_sdf_to_image_top_down(const VoxelBufferInternal &vb);
Array debug_print_sdf_y_slices(float scale) const;
Ref<Image> debug_print_sdf_y_slice(float scale, int y) const;
private:
void _b_deprecated_optimize();

View File

@ -776,6 +776,59 @@ float VoxelBufferInternal::get_sdf_quantization_scale(Depth d) {
}
}
void VoxelBufferInternal::get_range_f(float &out_min, float &out_max, ChannelId channel_index) const {
const Channel &channel = _channels[channel_index];
float min_value = get_voxel_f(0, 0, 0, channel_index);
float max_value = min_value;
if (channel.data == nullptr) {
out_min = min_value;
out_max = max_value;
return;
}
const uint64_t volume = get_volume();
switch (channel.depth) {
case DEPTH_8_BIT:
for (unsigned int i = 0; i < volume; ++i) {
const float v = s8_to_snorm(channel.data[i]);
min_value = math::min(v, min_value);
max_value = math::max(v, max_value);
}
break;
case DEPTH_16_BIT: {
const int16_t *data = reinterpret_cast<const int16_t *>(channel.data);
for (unsigned int i = 0; i < volume; ++i) {
const float v = s16_to_snorm(data[i]);
min_value = math::min(v, min_value);
max_value = math::max(v, max_value);
}
} break;
case DEPTH_32_BIT: {
const float *data = reinterpret_cast<const float *>(channel.data);
for (unsigned int i = 0; i < volume; ++i) {
const float v = data[i];
min_value = math::min(v, min_value);
max_value = math::max(v, max_value);
}
} break;
case DEPTH_64_BIT: {
const double *data = reinterpret_cast<const double *>(channel.data);
for (unsigned int i = 0; i < volume; ++i) {
const double v = data[i];
min_value = math::min(v, double(min_value));
max_value = math::max(v, double(max_value));
}
} break;
default:
CRASH_NOW();
}
out_min = min_value;
out_max = max_value;
}
const VoxelMetadata *VoxelBufferInternal::get_voxel_metadata(Vector3i pos) const {
ZN_ASSERT_RETURN_V(is_position_valid(pos), nullptr);
return _voxel_metadata.find(pos);

View File

@ -421,6 +421,8 @@ public:
// This returns that scale for a given depth configuration.
static float get_sdf_quantization_scale(Depth d);
void get_range_f(float &out_min, float &out_max, ChannelId channel_index) const;
// Metadata
VoxelMetadata &get_block_metadata() {