Merge branch 'master' into smooth_normalmaps
This commit is contained in:
commit
14f841fd7f
@ -28,6 +28,8 @@ Godot 4 is required from this version.
|
|||||||
- `VoxelGeneratorGraph`: editor: allow to change the axes on preview nodes 3D slices
|
- `VoxelGeneratorGraph`: editor: allow to change the axes on preview nodes 3D slices
|
||||||
- `VoxelGeneratorGraph`: editor: replace existing connection if dragging from/to an input port having one already
|
- `VoxelGeneratorGraph`: editor: replace existing connection if dragging from/to an input port having one already
|
||||||
- `VoxelGeneratorGraph`: editor: creating noise and curve nodes now auto-create their resource instead of coming up null
|
- `VoxelGeneratorGraph`: editor: creating noise and curve nodes now auto-create their resource instead of coming up null
|
||||||
|
- `VoxelGeneratorGraph`: editor: added pin button to keep the graph editor shown even after deselecting the terrain.
|
||||||
|
- `VoxelGeneratorGraph`: editor: added popout button to open the graph editor in a separate window
|
||||||
- `VoxelGeneratorGraph`: added `OutputSingleTexture` node for outputting a single texture index per voxel, as an alternative to weights. This is specific to smooth voxels.
|
- `VoxelGeneratorGraph`: added `OutputSingleTexture` node for outputting a single texture index per voxel, as an alternative to weights. This is specific to smooth voxels.
|
||||||
- `VoxelGeneratorGraph`: added math expression node
|
- `VoxelGeneratorGraph`: added math expression node
|
||||||
- `VoxelGeneratorGraph`: added Pow and Powi nodes
|
- `VoxelGeneratorGraph`: added Pow and Powi nodes
|
||||||
|
@ -168,7 +168,7 @@ struct SdfSubtract {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct SdfSet {
|
struct SdfSet {
|
||||||
float strength;
|
real_t strength;
|
||||||
inline real_t operator()(real_t a, real_t b) const {
|
inline real_t operator()(real_t a, real_t b) const {
|
||||||
return Math::lerp(a, b, strength);
|
return Math::lerp(a, b, strength);
|
||||||
}
|
}
|
||||||
@ -257,6 +257,8 @@ struct SdfBufferShape {
|
|||||||
// Outside the buffer
|
// Outside the buffer
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
// TODO Trilinear looks bad when the shape is scaled up.
|
||||||
|
// Use Hermite in 3D https://www.researchgate.net/publication/360206102_Hermite_interpolation_of_heightmaps
|
||||||
return interpolate_trilinear(buffer, buffer_size, lpos) * sdf_scale - isolevel;
|
return interpolate_trilinear(buffer, buffer_size, lpos) * sdf_scale - isolevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,7 +662,7 @@ Array separate_floating_chunks(VoxelTool &voxel_tool, Box3i world_box, Node *par
|
|||||||
collision_shape->set_position(offset);
|
collision_shape->set_position(offset);
|
||||||
|
|
||||||
RigidDynamicBody3D *rigid_body = memnew(RigidDynamicBody3D);
|
RigidDynamicBody3D *rigid_body = memnew(RigidDynamicBody3D);
|
||||||
rigid_body->set_transform(transform * local_transform.translated(-offset));
|
rigid_body->set_transform(transform * local_transform.translated_local(-offset));
|
||||||
rigid_body->add_child(collision_shape);
|
rigid_body->add_child(collision_shape);
|
||||||
rigid_body->set_freeze_mode(RigidDynamicBody3D::FREEZE_MODE_KINEMATIC);
|
rigid_body->set_freeze_mode(RigidDynamicBody3D::FREEZE_MODE_KINEMATIC);
|
||||||
rigid_body->set_freeze_enabled(true);
|
rigid_body->set_freeze_enabled(true);
|
||||||
@ -672,7 +672,7 @@ Array separate_floating_chunks(VoxelTool &voxel_tool, Box3i world_box, Node *par
|
|||||||
Timer *timer = memnew(Timer);
|
Timer *timer = memnew(Timer);
|
||||||
timer->set_wait_time(0.2);
|
timer->set_wait_time(0.2);
|
||||||
timer->set_one_shot(true);
|
timer->set_one_shot(true);
|
||||||
timer->connect("timeout", callable_mp(rigid_body, &RigidDynamicBody3D::set_freeze_enabled), varray(false));
|
timer->connect("timeout", callable_mp(rigid_body, &RigidDynamicBody3D::set_freeze_enabled).bind(false));
|
||||||
// Cannot use start() here because it requires to be inside the SceneTree,
|
// Cannot use start() here because it requires to be inside the SceneTree,
|
||||||
// and we don't know if it will be after we add to the parent.
|
// and we don't know if it will be after we add to the parent.
|
||||||
timer->set_autostart(true);
|
timer->set_autostart(true);
|
||||||
|
@ -411,7 +411,7 @@ void VoxelToolTerrain::run_blocky_random_tick(
|
|||||||
Callable::CallError error;
|
Callable::CallError error;
|
||||||
Variant retval; // We don't care about the return value, Callable API requires it
|
Variant retval; // We don't care about the return value, Callable API requires it
|
||||||
const CallbackData *cd = (const CallbackData *)self;
|
const CallbackData *cd = (const CallbackData *)self;
|
||||||
cd->callable.call(args, 2, retval, error);
|
cd->callable.callp(args, 2, retval, error);
|
||||||
// TODO I would really like to know what's the correct way to report such errors...
|
// TODO I would really like to know what's the correct way to report such errors...
|
||||||
// Examples I found in the engine are inconsistent
|
// Examples I found in the engine are inconsistent
|
||||||
ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, false);
|
ERR_FAIL_COND_V(error.error != Callable::CallError::CALL_OK, false);
|
||||||
@ -450,7 +450,7 @@ void VoxelToolTerrain::for_each_voxel_metadata_in_area(AABB voxel_area, const Ca
|
|||||||
const Variant *args[2] = { &key, &v };
|
const Variant *args[2] = { &key, &v };
|
||||||
Callable::CallError err;
|
Callable::CallError err;
|
||||||
Variant retval; // We don't care about the return value, Callable API requires it
|
Variant retval; // We don't care about the return value, Callable API requires it
|
||||||
callback.call(args, 2, retval, err);
|
callback.callp(args, 2, retval, err);
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK,
|
ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK,
|
||||||
String("Callable failed at {0}").format(varray(key)));
|
String("Callable failed at {0}").format(varray(key)));
|
||||||
|
@ -28,6 +28,8 @@ namespace zylann::voxel {
|
|||||||
const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected";
|
const char *VoxelGraphEditor::SIGNAL_NODE_SELECTED = "node_selected";
|
||||||
const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected";
|
const char *VoxelGraphEditor::SIGNAL_NOTHING_SELECTED = "nothing_selected";
|
||||||
const char *VoxelGraphEditor::SIGNAL_NODES_DELETED = "nodes_deleted";
|
const char *VoxelGraphEditor::SIGNAL_NODES_DELETED = "nodes_deleted";
|
||||||
|
const char *VoxelGraphEditor::SIGNAL_REGENERATE_REQUESTED = "regenerate_requested";
|
||||||
|
const char *VoxelGraphEditor::SIGNAL_POPOUT_REQUESTED = "popout_requested";
|
||||||
|
|
||||||
static NodePath to_node_path(StringName sn) {
|
static NodePath to_node_path(StringName sn) {
|
||||||
Vector<StringName> path;
|
Vector<StringName> path;
|
||||||
@ -88,6 +90,23 @@ VoxelGraphEditor::VoxelGraphEditor() {
|
|||||||
live_update_checkbox->connect("toggled", callable_mp(this, &VoxelGraphEditor::_on_live_update_toggled));
|
live_update_checkbox->connect("toggled", callable_mp(this, &VoxelGraphEditor::_on_live_update_toggled));
|
||||||
toolbar->add_child(live_update_checkbox);
|
toolbar->add_child(live_update_checkbox);
|
||||||
|
|
||||||
|
Control *spacer = memnew(Control);
|
||||||
|
spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
|
toolbar->add_child(spacer);
|
||||||
|
|
||||||
|
_pin_button = memnew(Button);
|
||||||
|
_pin_button->set_flat(true);
|
||||||
|
_pin_button->set_toggle_mode(true);
|
||||||
|
_pin_button->set_tooltip(TTR("Pin AnimationPlayer"));
|
||||||
|
toolbar->add_child(_pin_button);
|
||||||
|
//_pin_button->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_pin_pressed));
|
||||||
|
|
||||||
|
_popout_button = memnew(Button);
|
||||||
|
_popout_button->set_flat(true);
|
||||||
|
_popout_button->set_tooltip(TTR("Pop-out as separate window"));
|
||||||
|
_popout_button->connect("pressed", callable_mp(this, &VoxelGraphEditor::_on_popout_button_pressed));
|
||||||
|
toolbar->add_child(_popout_button);
|
||||||
|
|
||||||
vbox_container->add_child(toolbar);
|
vbox_container->add_child(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,6 +214,11 @@ void VoxelGraphEditor::_notification(int p_what) {
|
|||||||
case NOTIFICATION_VISIBILITY_CHANGED:
|
case NOTIFICATION_VISIBILITY_CHANGED:
|
||||||
set_process_internal(is_visible());
|
set_process_internal(is_visible());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NOTIFICATION_THEME_CHANGED:
|
||||||
|
_pin_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons")));
|
||||||
|
_popout_button->set_icon(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +306,7 @@ void VoxelGraphEditor::create_node_gui(uint32_t node_id) {
|
|||||||
|
|
||||||
VoxelGraphEditorNode *node_view = VoxelGraphEditorNode::create(**_graph, node_id);
|
VoxelGraphEditorNode *node_view = VoxelGraphEditorNode::create(**_graph, node_id);
|
||||||
node_view->set_name(ui_node_name);
|
node_view->set_name(ui_node_name);
|
||||||
node_view->connect("dragged", callable_mp(this, &VoxelGraphEditor::_on_graph_node_dragged), varray(node_id));
|
node_view->connect("dragged", callable_mp(this, &VoxelGraphEditor::_on_graph_node_dragged).bind(node_id));
|
||||||
|
|
||||||
_graph_edit->add_child(node_view);
|
_graph_edit->add_child(node_view);
|
||||||
}
|
}
|
||||||
@ -371,6 +395,10 @@ void VoxelGraphEditor::update_node_layout(uint32_t node_id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VoxelGraphEditor::is_pinned_hint() const {
|
||||||
|
return _pin_button->is_pressed();
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_nothing_selected(GraphEdit *graph_edit) {
|
static bool is_nothing_selected(GraphEdit *graph_edit) {
|
||||||
for (int i = 0; i < graph_edit->get_child_count(); ++i) {
|
for (int i = 0; i < graph_edit->get_child_count(); ++i) {
|
||||||
GraphNode *node = Object::cast_to<GraphNode>(graph_edit->get_child(i));
|
GraphNode *node = Object::cast_to<GraphNode>(graph_edit->get_child(i));
|
||||||
@ -659,9 +687,12 @@ void VoxelGraphEditor::update_previews(bool with_live_update) {
|
|||||||
|
|
||||||
if (hash != _last_output_graph_hash) {
|
if (hash != _last_output_graph_hash) {
|
||||||
_last_output_graph_hash = hash;
|
_last_output_graph_hash = hash;
|
||||||
// Re-generate the terrain.
|
|
||||||
// Only do that if the graph is valid.
|
// Not calling into `_voxel_node` directly because the editor could be pinned and the terrain not actually
|
||||||
_voxel_node->restart_stream();
|
// selected. In this situation the plugin may reset the node to null. But it is desirable for terrains
|
||||||
|
// using the current graph to update if they are in the edited scene, so this may be delegated to the editor
|
||||||
|
// plugin. There isn't enough context from here to do this cleanly.
|
||||||
|
emit_signal(SIGNAL_REGENERATE_REQUESTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -968,6 +999,14 @@ void VoxelGraphEditor::_on_live_update_toggled(bool enabled) {
|
|||||||
_live_update_enabled = enabled;
|
_live_update_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditor::_on_popout_button_pressed() {
|
||||||
|
emit_signal(SIGNAL_POPOUT_REQUESTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditor::set_popout_button_enabled(bool enable) {
|
||||||
|
_popout_button->set_visible(enable);
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelGraphEditor::_bind_methods() {
|
void VoxelGraphEditor::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("_check_nothing_selected"), &VoxelGraphEditor::_check_nothing_selected);
|
ClassDB::bind_method(D_METHOD("_check_nothing_selected"), &VoxelGraphEditor::_check_nothing_selected);
|
||||||
|
|
||||||
@ -977,8 +1016,10 @@ void VoxelGraphEditor::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("update_node_layout", "node_id"), &VoxelGraphEditor::update_node_layout);
|
ClassDB::bind_method(D_METHOD("update_node_layout", "node_id"), &VoxelGraphEditor::update_node_layout);
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo(SIGNAL_NODE_SELECTED, PropertyInfo(Variant::INT, "node_id")));
|
ADD_SIGNAL(MethodInfo(SIGNAL_NODE_SELECTED, PropertyInfo(Variant::INT, "node_id")));
|
||||||
ADD_SIGNAL(MethodInfo(SIGNAL_NOTHING_SELECTED, PropertyInfo(Variant::INT, "nothing_selected")));
|
ADD_SIGNAL(MethodInfo(SIGNAL_NOTHING_SELECTED));
|
||||||
ADD_SIGNAL(MethodInfo(SIGNAL_NODES_DELETED, PropertyInfo(Variant::INT, "nodes_deleted")));
|
ADD_SIGNAL(MethodInfo(SIGNAL_NODES_DELETED));
|
||||||
|
ADD_SIGNAL(MethodInfo(SIGNAL_REGENERATE_REQUESTED));
|
||||||
|
ADD_SIGNAL(MethodInfo(SIGNAL_POPOUT_REQUESTED));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace zylann::voxel
|
} // namespace zylann::voxel
|
||||||
|
@ -8,6 +8,7 @@ class GraphEdit;
|
|||||||
class PopupMenu;
|
class PopupMenu;
|
||||||
class AcceptDialog;
|
class AcceptDialog;
|
||||||
class UndoRedo;
|
class UndoRedo;
|
||||||
|
class Button;
|
||||||
|
|
||||||
namespace zylann::voxel {
|
namespace zylann::voxel {
|
||||||
|
|
||||||
@ -23,6 +24,8 @@ public:
|
|||||||
static const char *SIGNAL_NODE_SELECTED;
|
static const char *SIGNAL_NODE_SELECTED;
|
||||||
static const char *SIGNAL_NOTHING_SELECTED;
|
static const char *SIGNAL_NOTHING_SELECTED;
|
||||||
static const char *SIGNAL_NODES_DELETED;
|
static const char *SIGNAL_NODES_DELETED;
|
||||||
|
static const char *SIGNAL_REGENERATE_REQUESTED;
|
||||||
|
static const char *SIGNAL_POPOUT_REQUESTED;
|
||||||
|
|
||||||
VoxelGraphEditor();
|
VoxelGraphEditor();
|
||||||
|
|
||||||
@ -38,6 +41,9 @@ public:
|
|||||||
// Rebuilds the node's internal controls, and updates GUI connections going to it from the graph.
|
// Rebuilds the node's internal controls, and updates GUI connections going to it from the graph.
|
||||||
void update_node_layout(uint32_t node_id);
|
void update_node_layout(uint32_t node_id);
|
||||||
|
|
||||||
|
bool is_pinned_hint() const;
|
||||||
|
void set_popout_button_enabled(bool enable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
void _process(float delta);
|
void _process(float delta);
|
||||||
@ -74,6 +80,7 @@ private:
|
|||||||
void _on_preview_axes_menu_id_pressed(int id);
|
void _on_preview_axes_menu_id_pressed(int id);
|
||||||
void _on_generate_shader_button_pressed();
|
void _on_generate_shader_button_pressed();
|
||||||
void _on_live_update_toggled(bool enabled);
|
void _on_live_update_toggled(bool enabled);
|
||||||
|
void _on_popout_button_pressed();
|
||||||
|
|
||||||
void _check_nothing_selected();
|
void _check_nothing_selected();
|
||||||
|
|
||||||
@ -95,6 +102,8 @@ private:
|
|||||||
VoxelGraphEditorShaderDialog *_shader_dialog = nullptr;
|
VoxelGraphEditorShaderDialog *_shader_dialog = nullptr;
|
||||||
bool _live_update_enabled = false;
|
bool _live_update_enabled = false;
|
||||||
uint64_t _last_output_graph_hash = 0;
|
uint64_t _last_output_graph_hash = 0;
|
||||||
|
Button *_pin_button = nullptr;
|
||||||
|
Button *_popout_button = nullptr;
|
||||||
|
|
||||||
enum PreviewAxes { //
|
enum PreviewAxes { //
|
||||||
PREVIEW_XY = 0,
|
PREVIEW_XY = 0,
|
||||||
|
@ -10,6 +10,35 @@
|
|||||||
|
|
||||||
namespace zylann::voxel {
|
namespace zylann::voxel {
|
||||||
|
|
||||||
|
// TODO It would be really nice if we were not forced to use an AcceptDialog for making a window.
|
||||||
|
// AcceptDialog adds stuff I don't need, but Window is too low level.
|
||||||
|
class VoxelGraphEditorWindow : public AcceptDialog {
|
||||||
|
GDCLASS(VoxelGraphEditorWindow, AcceptDialog)
|
||||||
|
public:
|
||||||
|
VoxelGraphEditorWindow() {
|
||||||
|
set_exclusive(false);
|
||||||
|
set_close_on_escape(false);
|
||||||
|
get_ok_button()->hide();
|
||||||
|
set_min_size(Vector2(600, 300) * EDSCALE);
|
||||||
|
// I want the window to remain on top of the editor if the editor is given focus. `always_on_top` is the only
|
||||||
|
// property allowing that, but it requires `transient` to be `false`. Without `transient`, the window is no
|
||||||
|
// longer considered a child and won't give back focus to the editor when closed.
|
||||||
|
// So for now, the window will get hidden behind the editor if you click on the editor.
|
||||||
|
// you'll have to suffer moving popped out windows out of the editor area if you want to see them both...
|
||||||
|
//set_flag(Window::FLAG_ALWAYS_ON_TOP, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void _notification(int p_what) {
|
||||||
|
// switch (p_what) {
|
||||||
|
// case NOTIFICATION_WM_CLOSE_REQUEST:
|
||||||
|
// call_deferred(SNAME("hide"));
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
static void _bind_methods() {}
|
||||||
|
};
|
||||||
|
|
||||||
// Changes string editors of the inspector to call setters only when enter key is pressed, similar to Unreal.
|
// Changes string editors of the inspector to call setters only when enter key is pressed, similar to Unreal.
|
||||||
// Because the default behavior of `EditorPropertyText` is to call the setter on every character typed, which is a
|
// Because the default behavior of `EditorPropertyText` is to call the setter on every character typed, which is a
|
||||||
// nightmare when editing an Expression node: inputs change constantly as the code is written which has much higher
|
// nightmare when editing an Expression node: inputs change constantly as the code is written which has much higher
|
||||||
@ -41,6 +70,10 @@ VoxelGraphEditorPlugin::VoxelGraphEditorPlugin() {
|
|||||||
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nothing_selected));
|
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nothing_selected));
|
||||||
_graph_editor->connect(VoxelGraphEditor::SIGNAL_NODES_DELETED,
|
_graph_editor->connect(VoxelGraphEditor::SIGNAL_NODES_DELETED,
|
||||||
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nodes_deleted));
|
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_nodes_deleted));
|
||||||
|
_graph_editor->connect(VoxelGraphEditor::SIGNAL_REGENERATE_REQUESTED,
|
||||||
|
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_regenerate_requested));
|
||||||
|
_graph_editor->connect(VoxelGraphEditor::SIGNAL_POPOUT_REQUESTED,
|
||||||
|
callable_mp(this, &VoxelGraphEditorPlugin::_on_graph_editor_popout_requested));
|
||||||
_bottom_panel_button = add_control_to_bottom_panel(_graph_editor, TTR("Voxel Graph"));
|
_bottom_panel_button = add_control_to_bottom_panel(_graph_editor, TTR("Voxel Graph"));
|
||||||
_bottom_panel_button->hide();
|
_bottom_panel_button->hide();
|
||||||
|
|
||||||
@ -87,22 +120,36 @@ void VoxelGraphEditorPlugin::edit(Object *p_object) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_voxel_node = voxel_node;
|
||||||
_graph_editor->set_voxel_node(voxel_node);
|
_graph_editor->set_voxel_node(voxel_node);
|
||||||
|
|
||||||
|
if (_graph_editor_window != nullptr) {
|
||||||
|
update_graph_editor_window_title();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGraphEditorPlugin::make_visible(bool visible) {
|
void VoxelGraphEditorPlugin::make_visible(bool visible) {
|
||||||
|
if (_graph_editor_window != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
_bottom_panel_button->show();
|
_bottom_panel_button->show();
|
||||||
make_bottom_panel_item_visible(_graph_editor);
|
make_bottom_panel_item_visible(_graph_editor);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_bottom_panel_button->hide();
|
_voxel_node = nullptr;
|
||||||
_graph_editor->set_voxel_node(nullptr);
|
_graph_editor->set_voxel_node(nullptr);
|
||||||
|
|
||||||
// TODO Awful hack to handle the nonsense happening in `_on_graph_editor_node_selected`
|
const bool pinned = _graph_editor_window != nullptr || _graph_editor->is_pinned_hint();
|
||||||
if (!_deferred_visibility_scheduled) {
|
if (!pinned) {
|
||||||
_deferred_visibility_scheduled = true;
|
_bottom_panel_button->hide();
|
||||||
call_deferred("_hide_deferred");
|
|
||||||
|
// TODO Awful hack to handle the nonsense happening in `_on_graph_editor_node_selected`
|
||||||
|
if (!_deferred_visibility_scheduled) {
|
||||||
|
_deferred_visibility_scheduled = true;
|
||||||
|
call_deferred("_hide_deferred");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +199,92 @@ void VoxelGraphEditorPlugin::_on_graph_editor_nodes_deleted() {
|
|||||||
get_editor_interface()->inspect_object(*graph);
|
get_editor_interface()->inspect_object(*graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void for_each_node(Node *parent, F action) {
|
||||||
|
action(parent);
|
||||||
|
for (int i = 0; i < parent->get_child_count(); ++i) {
|
||||||
|
for_each_node(parent->get_child(i), action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditorPlugin::_on_graph_editor_regenerate_requested() {
|
||||||
|
// We could be editing the graph standalone with no terrain loaded
|
||||||
|
if (_voxel_node != nullptr) {
|
||||||
|
// Re-generate the selected terrain.
|
||||||
|
_voxel_node->restart_stream();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The node is not selected, but it might be in the tree
|
||||||
|
Node *root = get_editor_interface()->get_edited_scene_root();
|
||||||
|
|
||||||
|
if (root != nullptr) {
|
||||||
|
Ref<VoxelGeneratorGraph> generator = _graph_editor->get_graph();
|
||||||
|
ERR_FAIL_COND(generator.is_null());
|
||||||
|
|
||||||
|
for_each_node(root, [&generator](Node *node) {
|
||||||
|
VoxelNode *vnode = Object::cast_to<VoxelNode>(node);
|
||||||
|
if (vnode != nullptr && vnode->get_generator() == generator) {
|
||||||
|
vnode->restart_stream();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditorPlugin::_on_graph_editor_popout_requested() {
|
||||||
|
undock_graph_editor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditorPlugin::undock_graph_editor() {
|
||||||
|
ERR_FAIL_COND(_graph_editor_window != nullptr);
|
||||||
|
ZN_PRINT_VERBOSE("Undock voxel graph editor");
|
||||||
|
|
||||||
|
remove_control_from_bottom_panel(_graph_editor);
|
||||||
|
_bottom_panel_button = nullptr;
|
||||||
|
|
||||||
|
_graph_editor->set_popout_button_enabled(false);
|
||||||
|
_graph_editor->set_anchors_preset(Control::PRESET_FULL_RECT);
|
||||||
|
// I don't know what hides it but I needed to make it visible again
|
||||||
|
_graph_editor->show();
|
||||||
|
|
||||||
|
_graph_editor_window = memnew(VoxelGraphEditorWindow);
|
||||||
|
update_graph_editor_window_title();
|
||||||
|
_graph_editor_window->add_child(_graph_editor);
|
||||||
|
_graph_editor_window->connect("close_requested", callable_mp(this, &VoxelGraphEditorPlugin::dock_graph_editor));
|
||||||
|
|
||||||
|
Node *base_control = get_editor_interface()->get_base_control();
|
||||||
|
base_control->add_child(_graph_editor_window);
|
||||||
|
|
||||||
|
_graph_editor_window->popup_centered_ratio(0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditorPlugin::dock_graph_editor() {
|
||||||
|
ERR_FAIL_COND(_graph_editor_window == nullptr);
|
||||||
|
ZN_PRINT_VERBOSE("Dock voxel graph editor");
|
||||||
|
|
||||||
|
_graph_editor->get_parent()->remove_child(_graph_editor);
|
||||||
|
_graph_editor_window->queue_delete();
|
||||||
|
_graph_editor_window = nullptr;
|
||||||
|
|
||||||
|
_graph_editor->set_popout_button_enabled(true);
|
||||||
|
|
||||||
|
_bottom_panel_button = add_control_to_bottom_panel(_graph_editor, TTR("Voxel Graph"));
|
||||||
|
|
||||||
|
_bottom_panel_button->show();
|
||||||
|
make_bottom_panel_item_visible(_graph_editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VoxelGraphEditorPlugin::update_graph_editor_window_title() {
|
||||||
|
ERR_FAIL_COND(_graph_editor_window == nullptr);
|
||||||
|
String title;
|
||||||
|
if (_graph_editor->get_graph().is_valid()) {
|
||||||
|
title = _graph_editor->get_graph()->get_path();
|
||||||
|
title += " - ";
|
||||||
|
}
|
||||||
|
title += VoxelGeneratorGraph::get_class_static();
|
||||||
|
_graph_editor_window->set_title(title);
|
||||||
|
}
|
||||||
|
|
||||||
void VoxelGraphEditorPlugin::_bind_methods() {
|
void VoxelGraphEditorPlugin::_bind_methods() {
|
||||||
// ClassDB::bind_method(D_METHOD("_on_graph_editor_node_selected", "node_id"),
|
// ClassDB::bind_method(D_METHOD("_on_graph_editor_node_selected", "node_id"),
|
||||||
// &VoxelGraphEditorPlugin::_on_graph_editor_node_selected);
|
// &VoxelGraphEditorPlugin::_on_graph_editor_node_selected);
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
namespace zylann::voxel {
|
namespace zylann::voxel {
|
||||||
|
|
||||||
class VoxelGraphEditor;
|
class VoxelGraphEditor;
|
||||||
|
class VoxelNode;
|
||||||
|
class VoxelGraphEditorWindow;
|
||||||
|
|
||||||
class VoxelGraphEditorPlugin : public EditorPlugin {
|
class VoxelGraphEditorPlugin : public EditorPlugin {
|
||||||
GDCLASS(VoxelGraphEditorPlugin, EditorPlugin)
|
GDCLASS(VoxelGraphEditorPlugin, EditorPlugin)
|
||||||
@ -17,16 +19,24 @@ public:
|
|||||||
void make_visible(bool visible) override;
|
void make_visible(bool visible) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void undock_graph_editor();
|
||||||
|
void dock_graph_editor();
|
||||||
|
void update_graph_editor_window_title();
|
||||||
|
|
||||||
void _on_graph_editor_node_selected(uint32_t node_id);
|
void _on_graph_editor_node_selected(uint32_t node_id);
|
||||||
void _on_graph_editor_nothing_selected();
|
void _on_graph_editor_nothing_selected();
|
||||||
void _on_graph_editor_nodes_deleted();
|
void _on_graph_editor_nodes_deleted();
|
||||||
|
void _on_graph_editor_regenerate_requested();
|
||||||
|
void _on_graph_editor_popout_requested();
|
||||||
void _hide_deferred();
|
void _hide_deferred();
|
||||||
|
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
||||||
VoxelGraphEditor *_graph_editor = nullptr;
|
VoxelGraphEditor *_graph_editor = nullptr;
|
||||||
|
VoxelGraphEditorWindow *_graph_editor_window = nullptr;
|
||||||
Button *_bottom_panel_button = nullptr;
|
Button *_bottom_panel_button = nullptr;
|
||||||
bool _deferred_visibility_scheduled = false;
|
bool _deferred_visibility_scheduled = false;
|
||||||
|
VoxelNode *_voxel_node = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace zylann::voxel
|
} // namespace zylann::voxel
|
||||||
|
@ -196,7 +196,6 @@ bool VoxelGraphNodeInspectorWrapper::_set(const StringName &p_name, const Varian
|
|||||||
const uint32_t node_type_id = graph->get_node_type_id(_node_id);
|
const uint32_t node_type_id = graph->get_node_type_id(_node_id);
|
||||||
|
|
||||||
const VoxelGraphNodeDB &db = VoxelGraphNodeDB::get_singleton();
|
const VoxelGraphNodeDB &db = VoxelGraphNodeDB::get_singleton();
|
||||||
const bool autoconnect = graph->get_node_default_inputs_autoconnect(_node_id);
|
|
||||||
|
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
if (db.try_get_param_index_from_name(node_type_id, p_name, index)) {
|
if (db.try_get_param_index_from_name(node_type_id, p_name, index)) {
|
||||||
|
@ -50,8 +50,8 @@ void VoxelInstanceLibraryEditorInspectorPlugin::add_buttons() {
|
|||||||
button_remove->set_icon(icon_provider->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
button_remove->set_icon(icon_provider->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
|
||||||
button_remove->set_flat(true);
|
button_remove->set_flat(true);
|
||||||
button_remove->connect("pressed",
|
button_remove->connect("pressed",
|
||||||
callable_mp(button_listener, &VoxelInstanceLibraryEditorPlugin::_on_button_pressed),
|
callable_mp(button_listener, &VoxelInstanceLibraryEditorPlugin::_on_button_pressed)
|
||||||
varray(BUTTON_REMOVE_ITEM));
|
.bind(BUTTON_REMOVE_ITEM));
|
||||||
hb->add_child(button_remove);
|
hb->add_child(button_remove);
|
||||||
|
|
||||||
Control *spacer = memnew(Control);
|
Control *spacer = memnew(Control);
|
||||||
@ -61,8 +61,8 @@ void VoxelInstanceLibraryEditorInspectorPlugin::add_buttons() {
|
|||||||
Button *button_update = memnew(Button);
|
Button *button_update = memnew(Button);
|
||||||
button_update->set_text(TTR("Update From Scene..."));
|
button_update->set_text(TTR("Update From Scene..."));
|
||||||
button_update->connect("pressed",
|
button_update->connect("pressed",
|
||||||
callable_mp(button_listener, &VoxelInstanceLibraryEditorPlugin::_on_button_pressed),
|
callable_mp(button_listener, &VoxelInstanceLibraryEditorPlugin::_on_button_pressed)
|
||||||
varray(BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE));
|
.bind(BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE));
|
||||||
hb->add_child(button_update);
|
hb->add_child(button_update);
|
||||||
|
|
||||||
add_custom_control(hb);
|
add_custom_control(hb);
|
||||||
|
@ -23,7 +23,7 @@ EditorPropertyAABBMinMax::EditorPropertyAABBMinMax() {
|
|||||||
EditorSpinSlider *sb = memnew(EditorSpinSlider);
|
EditorSpinSlider *sb = memnew(EditorSpinSlider);
|
||||||
sb->set_flat(true);
|
sb->set_flat(true);
|
||||||
sb->set_h_size_flags(SIZE_EXPAND_FILL);
|
sb->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
sb->connect("value_changed", callable_mp(this, &EditorPropertyAABBMinMax::_value_changed), varray(i));
|
sb->connect("value_changed", callable_mp(this, &EditorPropertyAABBMinMax::_value_changed).bind(i));
|
||||||
_spinboxes[i] = sb;
|
_spinboxes[i] = sb;
|
||||||
|
|
||||||
add_focusable(sb);
|
add_focusable(sb);
|
||||||
|
@ -156,9 +156,9 @@ void VoxelTerrainEditorPlugin::set_node(VoxelNode *node) {
|
|||||||
|
|
||||||
if (_node != nullptr) {
|
if (_node != nullptr) {
|
||||||
_node->connect(
|
_node->connect(
|
||||||
"tree_entered", callable_mp(this, &VoxelTerrainEditorPlugin::_on_terrain_tree_entered), varray(_node));
|
"tree_entered", callable_mp(this, &VoxelTerrainEditorPlugin::_on_terrain_tree_entered).bind(_node));
|
||||||
_node->connect(
|
_node->connect(
|
||||||
"tree_exited", callable_mp(this, &VoxelTerrainEditorPlugin::_on_terrain_tree_exited), varray(_node));
|
"tree_exited", callable_mp(this, &VoxelTerrainEditorPlugin::_on_terrain_tree_exited).bind(_node));
|
||||||
|
|
||||||
VoxelLodTerrain *vlt = Object::cast_to<VoxelLodTerrain>(_node);
|
VoxelLodTerrain *vlt = Object::cast_to<VoxelLodTerrain>(_node);
|
||||||
|
|
||||||
|
@ -357,7 +357,7 @@ Error VoxelVoxMeshImporter::import(const String &p_source_file, const String &p_
|
|||||||
{
|
{
|
||||||
ZN_PROFILE_SCOPE();
|
ZN_PROFILE_SCOPE();
|
||||||
String mesh_save_path = String("{0}.mesh").format(varray(p_save_path));
|
String mesh_save_path = String("{0}.mesh").format(varray(p_save_path));
|
||||||
const Error mesh_save_err = ResourceSaver::save(mesh_save_path, mesh);
|
const Error mesh_save_err = ResourceSaver::save(mesh, mesh_save_path);
|
||||||
ERR_FAIL_COND_V_MSG(
|
ERR_FAIL_COND_V_MSG(
|
||||||
mesh_save_err != OK, mesh_save_err, String("Failed to save {0}").format(varray(mesh_save_path)));
|
mesh_save_err != OK, mesh_save_err, String("Failed to save {0}").format(varray(mesh_save_path)));
|
||||||
}
|
}
|
||||||
|
@ -373,7 +373,7 @@ Error VoxelVoxSceneImporter::import(const String &p_source_file, const String &p
|
|||||||
String res_save_path = String("{0}.model{1}.mesh").format(varray(p_save_path, model_index));
|
String res_save_path = String("{0}.model{1}.mesh").format(varray(p_save_path, model_index));
|
||||||
// `FLAG_CHANGE_PATH` did not do what I thought it did.
|
// `FLAG_CHANGE_PATH` did not do what I thought it did.
|
||||||
mesh->set_path(res_save_path);
|
mesh->set_path(res_save_path);
|
||||||
const Error mesh_save_err = ResourceSaver::save(res_save_path, mesh);
|
const Error mesh_save_err = ResourceSaver::save(mesh, res_save_path);
|
||||||
ERR_FAIL_COND_V_MSG(
|
ERR_FAIL_COND_V_MSG(
|
||||||
mesh_save_err != OK, mesh_save_err, String("Failed to save {0}").format(varray(res_save_path)));
|
mesh_save_err != OK, mesh_save_err, String("Failed to save {0}").format(varray(res_save_path)));
|
||||||
}
|
}
|
||||||
@ -387,7 +387,7 @@ Error VoxelVoxSceneImporter::import(const String &p_source_file, const String &p
|
|||||||
scene.instantiate();
|
scene.instantiate();
|
||||||
scene->pack(root_node);
|
scene->pack(root_node);
|
||||||
String scene_save_path = p_save_path + ".tscn";
|
String scene_save_path = p_save_path + ".tscn";
|
||||||
const Error save_err = ResourceSaver::save(scene_save_path, scene);
|
const Error save_err = ResourceSaver::save(scene, scene_save_path);
|
||||||
memdelete(root_node);
|
memdelete(root_node);
|
||||||
ERR_FAIL_COND_V_MSG(save_err != OK, save_err, "Cannot save scene to file '" + scene_save_path);
|
ERR_FAIL_COND_V_MSG(save_err != OK, save_err, "Cannot save scene to file '" + scene_save_path);
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ static bool is_node_equivalent(const ProgramGraph &graph, const ProgramGraph::No
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ZN_ASSERT_RETURN_V_MSG(
|
ZN_ASSERT_RETURN_V_MSG(
|
||||||
node1_input.connections.size() <= 1, "Multiple input connections isn't supported", false);
|
node1_input.connections.size() <= 1, false, "Multiple input connections isn't supported");
|
||||||
// TODO Some nodes like `*` and `+` have unordered inputs, we need to handle that
|
// TODO Some nodes like `*` and `+` have unordered inputs, we need to handle that
|
||||||
if (node1_input.connections.size() == 0) {
|
if (node1_input.connections.size() == 0) {
|
||||||
// No ancestor, check default inputs (autoconnect is ignored, it must have been applied earlier)
|
// No ancestor, check default inputs (autoconnect is ignored, it must have been applied earlier)
|
||||||
|
@ -152,7 +152,7 @@ void VoxelBuffer::for_each_voxel_metadata(const Callable &callback) const {
|
|||||||
const Variant *args[2] = { &key, &v };
|
const Variant *args[2] = { &key, &v };
|
||||||
Callable::CallError err;
|
Callable::CallError err;
|
||||||
Variant retval; // We don't care about the return value, Callable API requires it
|
Variant retval; // We don't care about the return value, Callable API requires it
|
||||||
callback.call(args, 2, retval, err);
|
callback.callp(args, 2, retval, err);
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(
|
ERR_FAIL_COND_MSG(
|
||||||
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
|
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
|
||||||
@ -174,7 +174,7 @@ void VoxelBuffer::for_each_voxel_metadata_in_area(const Callable &callback, Vect
|
|||||||
const Variant *args[2] = { &key, &v };
|
const Variant *args[2] = { &key, &v };
|
||||||
Callable::CallError err;
|
Callable::CallError err;
|
||||||
Variant retval; // We don't care about the return value, Callable API requires it
|
Variant retval; // We don't care about the return value, Callable API requires it
|
||||||
callback.call(args, 2, retval, err);
|
callback.callp(args, 2, retval, err);
|
||||||
|
|
||||||
ERR_FAIL_COND_MSG(
|
ERR_FAIL_COND_MSG(
|
||||||
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
|
err.error != Callable::CallError::CALL_OK, String("Callable failed at {0}").format(varray(key)));
|
||||||
|
@ -1642,7 +1642,7 @@ void VoxelInstancer::debug_dump_as_scene(String fpath) const {
|
|||||||
memdelete(root);
|
memdelete(root);
|
||||||
ERR_FAIL_COND(pack_result != OK);
|
ERR_FAIL_COND(pack_result != OK);
|
||||||
|
|
||||||
const Error save_result = ResourceSaver::save(fpath, packed_scene, ResourceSaver::FLAG_BUNDLE_RESOURCES);
|
const Error save_result = ResourceSaver::save(packed_scene, fpath, ResourceSaver::FLAG_BUNDLE_RESOURCES);
|
||||||
ERR_FAIL_COND(save_result != OK);
|
ERR_FAIL_COND(save_result != OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2609,7 +2609,7 @@ Error VoxelLodTerrain::_b_debug_dump_as_scene(String fpath, bool include_instanc
|
|||||||
return pack_result;
|
return pack_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Error save_result = ResourceSaver::save(fpath, scene, ResourceSaver::FLAG_BUNDLE_RESOURCES);
|
const Error save_result = ResourceSaver::save(scene, fpath, ResourceSaver::FLAG_BUNDLE_RESOURCES);
|
||||||
return save_result;
|
return save_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user