Added inspector plugin to edit VoxelMeshSDF
This commit is contained in:
parent
3289b44136
commit
f993796ca1
1
SCsub
1
SCsub
@ -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
|
||||
|
||||
|
51
editor/mesh_sdf/voxel_mesh_sdf_editor_plugin.cpp
Normal file
51
editor/mesh_sdf/voxel_mesh_sdf_editor_plugin.cpp
Normal 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
|
32
editor/mesh_sdf/voxel_mesh_sdf_editor_plugin.h
Normal file
32
editor/mesh_sdf/voxel_mesh_sdf_editor_plugin.h
Normal 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
|
105
editor/mesh_sdf/voxel_mesh_sdf_viewer.cpp
Normal file
105
editor/mesh_sdf/voxel_mesh_sdf_viewer.cpp
Normal 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
|
36
editor/mesh_sdf/voxel_mesh_sdf_viewer.h
Normal file
36
editor/mesh_sdf/voxel_mesh_sdf_viewer.h
Normal 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
|
@ -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
|
||||
|
@ -219,13 +219,11 @@ Ref<Image> VoxelBuffer::debug_print_sdf_to_image_top_down(const VoxelBufferInter
|
||||
return im;
|
||||
}
|
||||
|
||||
Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
|
||||
Array images;
|
||||
|
||||
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>());
|
||||
|
||||
for (int y = 0; y < res.y; ++y) {
|
||||
Ref<Image> im;
|
||||
im.instantiate();
|
||||
im->create(res.x, res.z, false, Image::FORMAT_RGB8);
|
||||
@ -246,7 +244,17 @@ Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
|
||||
}
|
||||
}
|
||||
|
||||
images.append(im);
|
||||
return im;
|
||||
}
|
||||
|
||||
Array VoxelBuffer::debug_print_sdf_y_slices(float scale) const {
|
||||
Array images;
|
||||
|
||||
const VoxelBufferInternal &buffer = *_buffer;
|
||||
const Vector3i res = buffer.get_size();
|
||||
|
||||
for (int y = 0; y < res.y; ++y) {
|
||||
images.append(debug_print_sdf_y_slice(scale, y));
|
||||
}
|
||||
|
||||
return images;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user