Moved VoxelInstanceLibrary menu to the inspector
parent
f86371af56
commit
8ab6abe04a
|
@ -34,6 +34,7 @@ Ongoing development - `master`
|
|||
- `VoxelInstancer`: added menu to setup a multimesh item from a scene (similarly to GridMap), which also allows to set up colliders
|
||||
- `VoxelInstancer`: added initial support for instancing regular scenes (slower than multimeshes)
|
||||
- `VoxelInstancer`: added option to turn off random rotation
|
||||
- `VoxelInstanceLibrary`: moved menu to add/remove/update items to the inspector, instead of the 3D editor toolbar
|
||||
|
||||
- Breaking changes
|
||||
- `VoxelBuffer`: channels `DATA3` and `DATA4` were renamed `INDICES` and `WEIGHTS`
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 17 KiB |
|
@ -4,6 +4,7 @@ Instancing
|
|||
The module provides an instancing system with the [VoxelInstancer](api/VoxelInstancer.md) node. This node must be added as child of a voxel terrain. It allows to spawn 3D models on top of the terrain's surface, which can later be removed when modified.
|
||||
|
||||
It can spawn two different kinds of objects:
|
||||
|
||||
- **Multimesh instances**. They can be extremely numerous, and can optionally have collision.
|
||||
- **Scene instances**. They use regular scenes, however it is much slower so should be tuned to low numbers.
|
||||
|
||||
|
@ -19,11 +20,11 @@ VoxelInstanceLibrary
|
|||
|
||||
In order to spawn items, `VoxelInstancer` needs a [VoxelInstanceLibrary](api/VoxelInstanceLibrary.md) resource. This resource contains a list of all the items that can be spawned, and how they will be placed.
|
||||
|
||||
Select a `VoxelInstancer`. In the inspector, assign a library to the `library` property, or create a new embedded one. Then click on the library resource. Now a menu should show up in the top bar of the main viewport:
|
||||
Select a `VoxelInstancer`. In the inspector, assign a library to the `library` property, or create a new embedded one. Then click on the library resource. Buttons appear at the top of the inspector:
|
||||
|
||||
![Screenshot of the VoxelInstanceLibrary menu](images/instance_library_menu.png)
|
||||
|
||||
In this menu, you can add items to the library by clicking `VoxelInstanceLibrary -> Add Multimesh item`.
|
||||
You can add items to the library by clicking the "+" icon, and choose `Add Multimesh item`.
|
||||
|
||||
Items created this way come with a default setup, so you should be able to see something appear on top of the voxel surface.
|
||||
|
||||
|
@ -97,7 +98,7 @@ The save format is described in [this document](specs/instances_format.md).
|
|||
|
||||
### Setting up a Multimesh item from a scene
|
||||
|
||||
It is possible to setup a Multimesh Item from an existing scene, as an alternative to setting it up in the inspector. One reason you could need this is to setup colliders, because although they are supported, it is not possible to set them in the inspector at the moment. It might also be more convenient to design instances in the 3D editor using nodes.
|
||||
It is possible to setup a Multimesh Item from an existing scene, as an alternative to setting it up in the inspector. One reason you could need this is to setup colliders, because although they are supported, it is not possible to set them in the inspector at the moment. It is also more convenient to design instances in the 3D editor using nodes.
|
||||
|
||||
This conversion process expects your scene to follow a specific structure:
|
||||
|
||||
|
|
|
@ -6,21 +6,66 @@
|
|||
#include <scene/gui/menu_button.h>
|
||||
#include <scene/resources/primitive_meshes.h>
|
||||
|
||||
VoxelInstanceLibraryEditorPlugin::VoxelInstanceLibraryEditorPlugin(EditorNode *p_node) {
|
||||
_menu_button = memnew(MenuButton);
|
||||
_menu_button->set_text(TTR("VoxelInstanceLibrary"));
|
||||
// TODO Icon
|
||||
//_menu_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshLibrary", "EditorIcons"));
|
||||
_menu_button->get_popup()->add_item(TTR("Add Multimesh Item (fast)"), MENU_ADD_MULTIMESH_ITEM);
|
||||
_menu_button->get_popup()->add_item(TTR("Update Multimesh Item From Scene"), MENU_UPDATE_MULTIMESH_ITEM_FROM_SCENE);
|
||||
_menu_button->get_popup()->add_separator();
|
||||
_menu_button->get_popup()->add_item(TTR("Add Scene Item (slow)"), MENU_ADD_SCENE_ITEM);
|
||||
_menu_button->get_popup()->add_separator();
|
||||
_menu_button->get_popup()->add_item(TTR("Remove Selected Item"), MENU_REMOVE_ITEM);
|
||||
// TODO Add and update from scene
|
||||
_menu_button->get_popup()->connect("id_pressed", this, "_on_menu_id_pressed");
|
||||
_menu_button->hide();
|
||||
namespace {
|
||||
enum Buttons {
|
||||
BUTTON_ADD_MULTIMESH_ITEM,
|
||||
BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE,
|
||||
BUTTON_ADD_SCENE_ITEM,
|
||||
BUTTON_REMOVE_ITEM
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool VoxelInstanceLibraryEditorInspectorPlugin::can_handle(Object *p_object) {
|
||||
return Object::cast_to<VoxelInstanceLibrary>(p_object) != nullptr;
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorInspectorPlugin::parse_begin(Object *p_object) {
|
||||
// TODO How can I make sure the buttons will be at the beginning of the "VoxelInstanceLibrary" category?
|
||||
// This is a better place than the Spatial editor toolbar (which would get hidden if you are not in the 3D tab
|
||||
// of the editor), but it will appear at the very top of the inspector, even above the "VoxelInstanceLibrary"
|
||||
// catgeory of properties. That looks a bit off, and if the class were to be inherited, it would start to be
|
||||
// confusing because these buttons are about the property list of "VoxelInstanceLibrary" specifically.
|
||||
// I could neither use `parse_property` nor `parse_category`, because when the list is empty,
|
||||
// the class returns no properties AND no category.
|
||||
add_buttons();
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorInspectorPlugin::add_buttons() {
|
||||
CRASH_COND(icon_provider == nullptr);
|
||||
CRASH_COND(button_listener == nullptr);
|
||||
|
||||
// Put buttons on top of the list of items
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
|
||||
MenuButton *button_add = memnew(MenuButton);
|
||||
button_add->set_icon(icon_provider->get_icon("Add", "EditorIcons"));
|
||||
button_add->get_popup()->add_item("MultiMesh item (fast)", BUTTON_ADD_MULTIMESH_ITEM);
|
||||
button_add->get_popup()->add_item("Scene item (slow)", BUTTON_ADD_SCENE_ITEM);
|
||||
button_add->get_popup()->connect("id_pressed", button_listener, "_on_button_pressed");
|
||||
hb->add_child(button_add);
|
||||
|
||||
Button *button_remove = memnew(Button);
|
||||
button_remove->set_icon(icon_provider->get_icon("Remove", "EditorIcons"));
|
||||
button_remove->set_flat(true);
|
||||
button_remove->connect("pressed", button_listener, "_on_button_pressed", varray(BUTTON_REMOVE_ITEM));
|
||||
hb->add_child(button_remove);
|
||||
|
||||
Control *spacer = memnew(Control);
|
||||
spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
hb->add_child(spacer);
|
||||
|
||||
Button *button_update = memnew(Button);
|
||||
button_update->set_text(TTR("Update From Scene..."));
|
||||
button_update->connect("pressed", button_listener, "_on_button_pressed",
|
||||
varray(BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE));
|
||||
hb->add_child(button_update);
|
||||
|
||||
add_custom_control(hb);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VoxelInstanceLibraryEditorPlugin::VoxelInstanceLibraryEditorPlugin(EditorNode *p_node) {
|
||||
Control *base_control = get_editor_interface()->get_base_control();
|
||||
|
||||
_confirmation_dialog = memnew(ConfirmationDialog);
|
||||
|
@ -39,11 +84,6 @@ VoxelInstanceLibraryEditorPlugin::VoxelInstanceLibraryEditorPlugin(EditorNode *p
|
|||
_open_scene_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
|
||||
base_control->add_child(_open_scene_dialog);
|
||||
_open_scene_dialog->connect("file_selected", this, "_on_open_scene_dialog_file_selected");
|
||||
|
||||
// TODO Perhaps it's a better idea to put this menu in the inspector directly?
|
||||
// Because it won't be visible if the user is in 2D or script mode,
|
||||
// and it's confusing to see it outside the inspector
|
||||
add_control_to_container(EditorPlugin::CONTAINER_SPATIAL_EDITOR_MENU, _menu_button);
|
||||
}
|
||||
|
||||
bool VoxelInstanceLibraryEditorPlugin::handles(Object *p_object) const {
|
||||
|
@ -56,15 +96,27 @@ void VoxelInstanceLibraryEditorPlugin::edit(Object *p_object) {
|
|||
_library.reference_ptr(lib);
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::make_visible(bool visible) {
|
||||
_menu_button->set_visible(visible);
|
||||
void VoxelInstanceLibraryEditorPlugin::_notification(int p_what) {
|
||||
if (p_what == NOTIFICATION_ENTER_TREE) {
|
||||
Control *base_control = get_editor_interface()->get_base_control();
|
||||
_inspector_plugin.instance();
|
||||
_inspector_plugin->button_listener = this;
|
||||
_inspector_plugin->icon_provider = base_control;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed(int id) {
|
||||
_last_used_menu_option = MenuOption(id);
|
||||
void VoxelInstanceLibraryEditorPlugin::_on_button_pressed(int id) {
|
||||
_last_used_button = id;
|
||||
|
||||
switch (id) {
|
||||
case MENU_ADD_MULTIMESH_ITEM: {
|
||||
case BUTTON_ADD_MULTIMESH_ITEM: {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
|
||||
Ref<VoxelInstanceLibraryItem> item;
|
||||
|
@ -87,7 +139,7 @@ void VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed(int id) {
|
|||
ur.commit_action();
|
||||
} break;
|
||||
|
||||
case MENU_UPDATE_MULTIMESH_ITEM_FROM_SCENE: {
|
||||
case BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE: {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
const int item_id = try_get_selected_item_id();
|
||||
if (item_id != -1) {
|
||||
|
@ -96,11 +148,11 @@ void VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed(int id) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case MENU_ADD_SCENE_ITEM: {
|
||||
case BUTTON_ADD_SCENE_ITEM: {
|
||||
_open_scene_dialog->popup_centered_ratio();
|
||||
} break;
|
||||
|
||||
case MENU_REMOVE_ITEM: {
|
||||
case BUTTON_REMOVE_ITEM: {
|
||||
ERR_FAIL_COND(_library.is_null());
|
||||
const int item_id = try_get_selected_item_id();
|
||||
if (item_id != -1) {
|
||||
|
@ -155,12 +207,12 @@ void VoxelInstanceLibraryEditorPlugin::_on_remove_item_confirmed() {
|
|||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_on_open_scene_dialog_file_selected(String fpath) {
|
||||
switch (_last_used_menu_option) {
|
||||
case MENU_ADD_SCENE_ITEM:
|
||||
switch (_last_used_button) {
|
||||
case BUTTON_ADD_SCENE_ITEM:
|
||||
add_scene_item(fpath);
|
||||
break;
|
||||
|
||||
case MENU_UPDATE_MULTIMESH_ITEM_FROM_SCENE:
|
||||
case BUTTON_UPDATE_MULTIMESH_ITEM_FROM_SCENE:
|
||||
update_multimesh_item_from_scene(fpath, _item_id_to_update);
|
||||
break;
|
||||
|
||||
|
@ -223,7 +275,7 @@ void VoxelInstanceLibraryEditorPlugin::update_multimesh_item_from_scene(String f
|
|||
}
|
||||
|
||||
void VoxelInstanceLibraryEditorPlugin::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_on_menu_id_pressed", "id"), &VoxelInstanceLibraryEditorPlugin::_on_menu_id_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_on_button_pressed", "id"), &VoxelInstanceLibraryEditorPlugin::_on_button_pressed);
|
||||
ClassDB::bind_method(D_METHOD("_on_remove_item_confirmed"),
|
||||
&VoxelInstanceLibraryEditorPlugin::_on_remove_item_confirmed);
|
||||
ClassDB::bind_method(D_METHOD("_on_open_scene_dialog_file_selected", "fpath"),
|
||||
|
|
|
@ -4,8 +4,23 @@
|
|||
#include "../../terrain/instancing/voxel_instance_library.h"
|
||||
#include <editor/editor_plugin.h>
|
||||
|
||||
class Control;
|
||||
class MenuButton;
|
||||
class ConfirmationDialog;
|
||||
class VoxelInstanceLibraryEditorPlugin;
|
||||
|
||||
class VoxelInstanceLibraryEditorInspectorPlugin : public EditorInspectorPlugin {
|
||||
GDCLASS(VoxelInstanceLibraryEditorInspectorPlugin, EditorInspectorPlugin)
|
||||
public:
|
||||
Control *icon_provider = nullptr;
|
||||
VoxelInstanceLibraryEditorPlugin *button_listener = nullptr;
|
||||
|
||||
bool can_handle(Object *p_object) override;
|
||||
void parse_begin(Object *p_object) override;
|
||||
|
||||
private:
|
||||
void add_buttons();
|
||||
};
|
||||
|
||||
class VoxelInstanceLibraryEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(VoxelInstanceLibraryEditorPlugin, EditorPlugin)
|
||||
|
@ -16,35 +31,29 @@ public:
|
|||
|
||||
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);
|
||||
|
||||
int try_get_selected_item_id();
|
||||
void add_scene_item(String fpath);
|
||||
void update_multimesh_item_from_scene(String fpath, int item_id);
|
||||
|
||||
void _on_menu_id_pressed(int id);
|
||||
void _on_button_pressed(int id);
|
||||
void _on_remove_item_confirmed();
|
||||
void _on_open_scene_dialog_file_selected(String fpath);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
enum MenuOption {
|
||||
MENU_ADD_MULTIMESH_ITEM,
|
||||
MENU_UPDATE_MULTIMESH_ITEM_FROM_SCENE,
|
||||
MENU_ADD_SCENE_ITEM,
|
||||
MENU_REMOVE_ITEM
|
||||
};
|
||||
|
||||
MenuButton *_menu_button = nullptr;
|
||||
ConfirmationDialog *_confirmation_dialog = nullptr;
|
||||
AcceptDialog *_info_dialog = nullptr;
|
||||
int _item_id_to_remove = -1;
|
||||
int _item_id_to_update = -1;
|
||||
EditorFileDialog *_open_scene_dialog;
|
||||
MenuOption _last_used_menu_option;
|
||||
int _last_used_button;
|
||||
|
||||
Ref<VoxelInstanceLibrary> _library;
|
||||
Ref<VoxelInstanceLibraryEditorInspectorPlugin> _inspector_plugin;
|
||||
};
|
||||
|
||||
#endif // VOXEL_INSTANCE_LIBRARY_EDITOR_PLUGIN_H
|
||||
|
|
Loading…
Reference in New Issue