Path: blob/master/editor/scene/3d/mesh_library_editor_plugin.cpp
9903 views
/**************************************************************************/1/* mesh_library_editor_plugin.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "mesh_library_editor_plugin.h"3132#include "editor/docks/inspector_dock.h"33#include "editor/editor_interface.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/gui/editor_file_dialog.h"37#include "editor/scene/3d/node_3d_editor_plugin.h"38#include "editor/settings/editor_settings.h"39#include "main/main.h"40#include "scene/3d/mesh_instance_3d.h"41#include "scene/3d/navigation/navigation_region_3d.h"42#include "scene/3d/physics/static_body_3d.h"43#include "scene/gui/menu_button.h"44#include "scene/resources/packed_scene.h"4546void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {47mesh_library = p_mesh_library;48if (mesh_library.is_valid()) {49menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), !mesh_library->has_meta("_editor_source_scene"));50}51}5253void MeshLibraryEditor::_menu_remove_confirm() {54switch (option) {55case MENU_OPTION_REMOVE_ITEM: {56mesh_library->remove_item(to_erase);57} break;58default: {59};60}61}6263void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {64cd_update->hide();65apply_xforms = p_apply_xforms;66String existing = mesh_library->get_meta("_editor_source_scene");67ERR_FAIL_COND(existing.is_empty());68_import_scene_cbk(existing);69}7071void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {72if (!p_merge) {73p_library->clear();74}7576HashMap<int, MeshInstance3D *> mesh_instances;7778for (int i = 0; i < p_scene->get_child_count(); i++) {79_import_scene_parse_node(p_library, mesh_instances, p_scene->get_child(i), p_merge, p_apply_xforms);80}8182//generate previews!8384if (true) {85Vector<Ref<Mesh>> meshes;86Vector<Transform3D> transforms;87Vector<int> ids = p_library->get_item_list();88for (int i = 0; i < ids.size(); i++) {89if (mesh_instances.find(ids[i])) {90meshes.push_back(p_library->get_item_mesh(ids[i]));91transforms.push_back(mesh_instances[ids[i]]->get_transform());92}93}9495Vector<Ref<Texture2D>> textures = EditorInterface::get_singleton()->make_mesh_previews(meshes, &transforms, EDITOR_GET("editors/grid_map/preview_size"));96int j = 0;97for (int i = 0; i < ids.size(); i++) {98if (mesh_instances.find(ids[i])) {99p_library->set_item_preview(ids[i], textures[j]);100j++;101}102}103}104}105106void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {107Ref<PackedScene> ps = ResourceLoader::load(p_str, "PackedScene");108ERR_FAIL_COND(ps.is_null());109Node *scene = ps->instantiate();110111ERR_FAIL_NULL_MSG(scene, "Cannot create an instance from PackedScene '" + p_str + "'.");112113_import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);114115memdelete(scene);116mesh_library->set_meta("_editor_source_scene", p_str);117118menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);119}120121void MeshLibraryEditor::_import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms) {122MeshInstance3D *mesh_instance_node = Object::cast_to<MeshInstance3D>(p_node);123124if (!mesh_instance_node) {125// No MeshInstance so search deeper ...126for (int i = 0; i < p_node->get_child_count(); i++) {127_import_scene_parse_node(p_library, p_mesh_instances, p_node->get_child(i), p_merge, p_apply_xforms);128}129return;130}131132Ref<Mesh> source_mesh = mesh_instance_node->get_mesh();133if (source_mesh.is_null()) {134return;135}136137int item_id = p_library->find_item_by_name(mesh_instance_node->get_name());138if (item_id < 0) {139item_id = p_library->get_last_unused_item_id();140p_library->create_item(item_id);141p_library->set_item_name(item_id, mesh_instance_node->get_name());142} else if (!p_merge) {143WARN_PRINT(vformat("MeshLibrary export found a MeshInstance3D with a duplicated name '%s' in the exported scene that overrides a previously parsed MeshInstance3D item with the same name.", mesh_instance_node->get_name()));144}145p_mesh_instances[item_id] = mesh_instance_node;146147Ref<Mesh> item_mesh = source_mesh->duplicate();148for (int i = 0; i < item_mesh->get_surface_count(); i++) {149Ref<Material> surface_override_material = mesh_instance_node->get_surface_override_material(i);150if (surface_override_material.is_valid()) {151item_mesh->surface_set_material(i, surface_override_material);152}153}154p_library->set_item_mesh(item_id, item_mesh);155156GeometryInstance3D::ShadowCastingSetting gi3d_cast_shadows_setting = mesh_instance_node->get_cast_shadows_setting();157switch (gi3d_cast_shadows_setting) {158case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF: {159p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF);160} break;161case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON: {162p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);163} break;164case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED: {165p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED);166} break;167case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY: {168p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY);169} break;170default: {171p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON);172} break;173}174175Transform3D item_mesh_transform;176if (p_apply_xforms) {177item_mesh_transform = mesh_instance_node->get_transform();178}179p_library->set_item_mesh_transform(item_id, item_mesh_transform);180181Vector<MeshLibrary::ShapeData> collisions;182for (int i = 0; i < mesh_instance_node->get_child_count(); i++) {183StaticBody3D *static_body_node = Object::cast_to<StaticBody3D>(mesh_instance_node->get_child(i));184if (!static_body_node) {185continue;186}187List<uint32_t> shapes;188static_body_node->get_shape_owners(&shapes);189for (uint32_t &E : shapes) {190if (static_body_node->is_shape_owner_disabled(E)) {191continue;192}193Transform3D shape_transform;194if (p_apply_xforms) {195shape_transform = mesh_instance_node->get_transform();196}197shape_transform *= static_body_node->get_transform() * static_body_node->shape_owner_get_transform(E);198for (int k = 0; k < static_body_node->shape_owner_get_shape_count(E); k++) {199Ref<Shape3D> collision_shape = static_body_node->shape_owner_get_shape(E, k);200if (collision_shape.is_null()) {201continue;202}203MeshLibrary::ShapeData shape_data;204shape_data.shape = collision_shape;205shape_data.local_transform = shape_transform;206collisions.push_back(shape_data);207}208}209}210p_library->set_item_shapes(item_id, collisions);211212for (int i = 0; i < mesh_instance_node->get_child_count(); i++) {213NavigationRegion3D *navigation_region_node = Object::cast_to<NavigationRegion3D>(mesh_instance_node->get_child(i));214if (!navigation_region_node) {215continue;216}217Ref<NavigationMesh> navigation_mesh = navigation_region_node->get_navigation_mesh();218if (navigation_mesh.is_valid()) {219Transform3D navigation_mesh_transform = navigation_region_node->get_transform();220p_library->set_item_navigation_mesh(item_id, navigation_mesh);221p_library->set_item_navigation_mesh_transform(item_id, navigation_mesh_transform);222break;223}224}225}226227Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {228_import_scene(p_base_scene, ml, p_merge, p_apply_xforms);229return OK;230}231232void MeshLibraryEditor::_menu_cbk(int p_option) {233option = p_option;234switch (p_option) {235case MENU_OPTION_ADD_ITEM: {236mesh_library->create_item(mesh_library->get_last_unused_item_id());237} break;238case MENU_OPTION_REMOVE_ITEM: {239String p = InspectorDock::get_inspector_singleton()->get_selected_path();240if (p.begins_with("item") && p.get_slice_count("/") >= 2) {241to_erase = p.get_slicec('/', 1).to_int();242cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));243cd_remove->popup_centered(Size2(300, 60));244}245} break;246case MENU_OPTION_IMPORT_FROM_SCENE: {247apply_xforms = false;248file->popup_file_dialog();249} break;250case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {251apply_xforms = true;252file->popup_file_dialog();253} break;254case MENU_OPTION_UPDATE_FROM_SCENE: {255cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));256cd_update->popup_centered(Size2(500, 60));257} break;258}259}260261MeshLibraryEditor::MeshLibraryEditor() {262file = memnew(EditorFileDialog);263file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);264//not for now?265List<String> extensions;266ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);267file->clear_filters();268file->set_title(TTR("Import Scene"));269for (const String &extension : extensions) {270file->add_filter("*." + extension, extension.to_upper());271}272add_child(file);273file->connect("file_selected", callable_mp(this, &MeshLibraryEditor::_import_scene_cbk));274275menu = memnew(MenuButton);276Node3DEditor::get_singleton()->add_control_to_menu_panel(menu);277menu->set_position(Point2(1, 1));278menu->set_text(TTR("MeshLibrary"));279menu->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MeshLibrary"), EditorStringName(EditorIcons)));280menu->set_flat(false);281menu->set_theme_type_variation("FlatMenuButton");282menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);283menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);284menu->get_popup()->add_separator();285menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);286menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);287menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);288menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);289menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MeshLibraryEditor::_menu_cbk));290menu->hide();291292cd_remove = memnew(ConfirmationDialog);293add_child(cd_remove);294cd_remove->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_remove_confirm));295cd_update = memnew(ConfirmationDialog);296add_child(cd_update);297cd_update->set_ok_button_text(TTR("Apply without Transforms"));298cd_update->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_update_confirm).bind(false));299cd_update->add_button(TTR("Apply with Transforms"))->connect(SceneStringName(pressed), callable_mp(this, &MeshLibraryEditor::_menu_update_confirm).bind(true));300}301302void MeshLibraryEditorPlugin::edit(Object *p_node) {303if (Object::cast_to<MeshLibrary>(p_node)) {304mesh_library_editor->edit(Object::cast_to<MeshLibrary>(p_node));305mesh_library_editor->show();306} else {307mesh_library_editor->edit(Ref<MeshLibrary>());308mesh_library_editor->hide();309}310}311312bool MeshLibraryEditorPlugin::handles(Object *p_node) const {313return p_node->is_class("MeshLibrary");314}315316void MeshLibraryEditorPlugin::make_visible(bool p_visible) {317if (p_visible) {318mesh_library_editor->show();319mesh_library_editor->get_menu_button()->show();320} else {321mesh_library_editor->hide();322mesh_library_editor->get_menu_button()->hide();323}324}325326MeshLibraryEditorPlugin::MeshLibraryEditorPlugin() {327mesh_library_editor = memnew(MeshLibraryEditor);328329EditorNode::get_singleton()->get_gui_base()->add_child(mesh_library_editor);330mesh_library_editor->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);331mesh_library_editor->set_end(Point2(0, 22));332mesh_library_editor->hide();333}334335336