Improved 3D Scene Importer

* Added option for importers to show an Advanced settings dialog
* Created advanced settings dialog for Scene Importer
* Cleaned up importers (remove many old/unused options)
* Added the ability to customize every node, material, mesh and animation individually
* Saving to animations and meshes to files is now a manual process, making it more predictable
* Added the ability for materials to be replaced by external files (or to be made external, up to you).
* When doubleclicking an impoted scene in the filesystem dock, it automatically shows the import settings instead of asking to open it.

WARNING: Lightmap UV unwrap is not working, it needs to be re-made.
This commit is contained in:
Juan Linietsky 2021-03-19 09:57:52 -03:00
parent 07f076fa4f
commit 97a3a66220
25 changed files with 2650 additions and 692 deletions

View file

@ -115,6 +115,9 @@ public:
ImportOption() {}
};
virtual bool has_advanced_options() const { return false; }
virtual void show_advanced_options(const String &p_path) {}
virtual int get_preset_count() const { return 0; }
virtual String get_preset_name(int p_idx) const { return String(); }

View file

@ -1668,7 +1668,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
return err;
}
void EditorFileSystem::_reimport_file(const String &p_file) {
void EditorFileSystem::_reimport_file(const String &p_file, const Map<StringName, Variant> *p_custom_options, const String &p_custom_importer) {
EditorFileSystemDirectory *fs = nullptr;
int cpos = -1;
bool found = _find_file(p_file, &fs, cpos);
@ -1677,23 +1677,32 @@ void EditorFileSystem::_reimport_file(const String &p_file) {
//try to obtain existing params
Map<StringName, Variant> params;
String importer_name;
String importer_name; //empty by default though
if (p_custom_importer != String()) {
importer_name = p_custom_importer;
}
if (p_custom_options != nullptr) {
params = *p_custom_options;
}
if (FileAccess::exists(p_file + ".import")) {
//use existing
Ref<ConfigFile> cf;
cf.instance();
Error err = cf->load(p_file + ".import");
if (err == OK) {
if (cf->has_section("params")) {
List<String> sk;
cf->get_section_keys("params", &sk);
for (List<String>::Element *E = sk.front(); E; E = E->next()) {
params[E->get()] = cf->get_value("params", E->get());
if (p_custom_options == nullptr) {
Ref<ConfigFile> cf;
cf.instance();
Error err = cf->load(p_file + ".import");
if (err == OK) {
if (cf->has_section("params")) {
List<String> sk;
cf->get_section_keys("params", &sk);
for (List<String>::Element *E = sk.front(); E; E = E->next()) {
params[E->get()] = cf->get_value("params", E->get());
}
}
if (p_custom_importer != String() && cf->has_section("remap")) {
importer_name = cf->get_value("remap", "importer");
}
}
if (cf->has_section("remap")) {
importer_name = cf->get_value("remap", "importer");
}
}
@ -1887,6 +1896,10 @@ void EditorFileSystem::_find_group_files(EditorFileSystemDirectory *efd, Map<Str
}
}
void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const Map<StringName, Variant> &p_custom_params) {
_reimport_file(p_file, &p_custom_params, p_importer);
}
void EditorFileSystem::reimport_files(const Vector<String> &p_files) {
{
// Ensure that ProjectSettings::IMPORTED_FILES_PATH exists.

View file

@ -203,7 +203,7 @@ class EditorFileSystem : public Node {
void _update_extensions();
void _reimport_file(const String &p_file);
void _reimport_file(const String &p_file, const Map<StringName, Variant> *p_custom_options = nullptr, const String &p_custom_importer = String());
Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
@ -257,6 +257,8 @@ public:
void reimport_files(const Vector<String> &p_files);
void reimport_file_with_custom_parameters(const String &p_file, const String &p_importer, const Map<StringName, Variant> &p_custom_params);
void update_script_classes();
bool is_group_file(const String &p_path) const;

View file

@ -102,6 +102,7 @@
#include "editor/import/resource_importer_texture.h"
#include "editor/import/resource_importer_texture_atlas.h"
#include "editor/import/resource_importer_wav.h"
#include "editor/import/scene_import_settings.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "editor/import_dock.h"
#include "editor/multi_node_edit.h"
@ -6179,6 +6180,9 @@ EditorNode::EditorNode() {
project_settings = memnew(ProjectSettingsEditor(&editor_data));
gui_base->add_child(project_settings);
scene_import_settings = memnew(SceneImportSettings);
gui_base->add_child(scene_import_settings);
export_template_manager = memnew(ExportTemplateManager);
gui_base->add_child(export_template_manager);

View file

@ -88,6 +88,7 @@ class Button;
class VSplitContainer;
class Window;
class SubViewport;
class SceneImportSettings;
class EditorNode : public Node {
GDCLASS(EditorNode, Node);
@ -410,6 +411,7 @@ private:
EditorResourcePreview *resource_preview;
EditorFolding editor_folding;
SceneImportSettings *scene_import_settings;
struct BottomPanelItem {
String name;
Control *control = nullptr;

View file

@ -945,7 +945,25 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit
}
} else if (fpath != "Favorites") {
if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
editor->open_request(fpath);
bool is_imported = false;
{
List<String> importer_exts;
ResourceImporterScene::get_singleton()->get_recognized_extensions(&importer_exts);
String extension = fpath.get_extension();
for (List<String>::Element *E = importer_exts.front(); E; E = E->next()) {
if (extension.nocasecmp_to(E->get())) {
is_imported = true;
break;
}
}
}
if (is_imported) {
ResourceImporterScene::get_singleton()->show_advanced_options(fpath);
} else {
editor->open_request(fpath);
}
} else {
editor->load_resource(fpath);
}

View file

@ -79,6 +79,9 @@ struct ColladaImport {
Vector<int> valid_animated_properties;
Map<String, bool> bones_with_animation;
Set<String> mesh_unique_names;
Set<String> material_unique_names;
Error _populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent);
Error _create_scene_skeletons(Collada::Node *p_node);
Error _create_scene(Collada::Node *p_node, Node3D *p_parent);
@ -326,12 +329,25 @@ Error ColladaImport::_create_material(const String &p_target) {
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
String base_name;
if (src_mat.name != "") {
material->set_name(src_mat.name);
base_name = src_mat.name;
} else if (effect.name != "") {
material->set_name(effect.name);
base_name = effect.name;
} else {
base_name = "Material";
}
String name = base_name;
int counter = 2;
while (material_unique_names.has(name)) {
name = base_name + itos(counter++);
}
material_unique_names.insert(name);
material->set_name(name);
// DIFFUSE
if (effect.diffuse.texture != "") {
@ -1128,7 +1144,22 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
mesh = Ref<EditorSceneImporterMesh>(memnew(EditorSceneImporterMesh));
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
mesh->set_name(meshdata.name);
String name = meshdata.name;
if (name == "") {
name = "Mesh";
}
int counter = 2;
while (mesh_unique_names.has(name)) {
name = meshdata.name;
if (name == "") {
name = "Mesh";
}
name += itos(counter++);
}
mesh_unique_names.insert(name);
mesh->set_name(name);
Error err = _create_mesh_surfaces(morphs.size() == 0, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, morph, morphs, p_use_compression, use_mesh_builtin_materials);
ERR_FAIL_COND_V_MSG(err, err, "Cannot create mesh surface.");
@ -1645,16 +1676,23 @@ void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) cons
}
Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
if (r_err) {
*r_err = OK;
}
ColladaImport state;
uint32_t flags = Collada::IMPORT_FLAG_SCENE;
if (p_flags & IMPORT_ANIMATION) {
flags |= Collada::IMPORT_FLAG_ANIMATION;
}
state.use_mesh_builtin_materials = !(p_flags & IMPORT_MATERIALS_IN_INSTANCES);
state.use_mesh_builtin_materials = true;
state.bake_fps = p_bake_fps;
Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & EditorSceneImporter::IMPORT_USE_COMPRESSION);
Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, 0);
if (r_err) {
*r_err = err;
}
ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'.");
@ -1674,7 +1712,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
}
if (p_flags & IMPORT_ANIMATION) {
state.create_animations(p_flags & IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
state.create_animations(true, true);
AnimationPlayer *ap = memnew(AnimationPlayer);
for (int i = 0; i < state.animations.size(); i++) {
String name;
@ -1684,12 +1722,6 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
name = state.animations[i]->get_name();
}
if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
state.animations.write[i]->set_loop(true);
}
}
ap->add_animation(name, state.animations[i]);
}
state.scene->add_child(ap);
@ -1707,7 +1739,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
state.create_animations(p_flags & EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
state.create_animations(true, true);
if (state.scene) {
memdelete(state.scene);
}
@ -1716,12 +1748,6 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
return Ref<Animation>();
}
Ref<Animation> anim = state.animations[0];
String base = p_path.get_basename().to_lower();
if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) {
anim->set_loop(true);
}
}
return anim;
}

View file

@ -427,7 +427,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, 0, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
if (err != OK) {
if (r_err) {

File diff suppressed because it is too large Load diff

View file

@ -39,7 +39,9 @@
#include "scene/resources/skin.h"
class Material;
class AnimationPlayer;
class EditorSceneImporterMesh;
class EditorSceneImporter : public Reference {
GDCLASS(EditorSceneImporter, Reference);
@ -53,15 +55,9 @@ public:
enum ImportFlags {
IMPORT_SCENE = 1,
IMPORT_ANIMATION = 2,
IMPORT_ANIMATION_DETECT_LOOP = 4,
IMPORT_ANIMATION_OPTIMIZE = 8,
IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS = 16,
IMPORT_ANIMATION_KEEP_VALUE_TRACKS = 32,
IMPORT_GENERATE_TANGENT_ARRAYS = 256,
IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512,
IMPORT_MATERIALS_IN_INSTANCES = 1024,
IMPORT_USE_COMPRESSION = 2048,
IMPORT_USE_NAMED_SKIN_BINDS = 4096,
IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
};
@ -76,17 +72,15 @@ public:
class EditorScenePostImport : public Reference {
GDCLASS(EditorScenePostImport, Reference);
String source_folder;
String source_file;
protected:
static void _bind_methods();
public:
String get_source_folder() const;
String get_source_file() const;
virtual Node *post_import(Node *p_scene);
virtual void init(const String &p_source_folder, const String &p_source_file);
virtual void init(const String &p_source_file);
EditorScenePostImport();
};
@ -97,31 +91,35 @@ class ResourceImporterScene : public ResourceImporter {
static ResourceImporterScene *singleton;
enum Presets {
PRESET_SEPARATE_MATERIALS,
PRESET_SEPARATE_MESHES,
PRESET_SEPARATE_ANIMATIONS,
PRESET_SINGLE_SCENE,
PRESET_SEPARATE_MESHES_AND_MATERIALS,
PRESET_SEPARATE_MESHES_AND_ANIMATIONS,
PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS,
PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS,
PRESET_MULTIPLE_SCENES,
PRESET_MULTIPLE_SCENES_AND_MATERIALS,
PRESET_MAX
};
enum LightBakeMode {
LIGHT_BAKE_DISABLED,
LIGHT_BAKE_ENABLE,
LIGHT_BAKE_LIGHTMAPS
LIGHT_BAKE_DYNAMIC,
LIGHT_BAKE_STATIC,
LIGHT_BAKE_STATIC_LIGHTMAPS
};
enum MeshPhysicsMode {
MESH_PHYSICS_DISABLED,
MESH_PHYSICS_MESH_AND_STATIC_COLLIDER,
MESH_PHYSICS_RIGID_BODY_AND_MESH,
MESH_PHYSICS_STATIC_COLLIDER_ONLY,
MESH_PHYSICS_AREA_ONLY,
};
enum NavMeshMode {
NAVMESH_DISABLED,
NAVMESH_MESH_AND_NAVMESH,
NAVMESH_NAVMESH_ONLY,
};
enum MeshOverride {
MESH_OVERRIDE_DEFAULT,
MESH_OVERRIDE_ENABLE,
MESH_OVERRIDE_DISABLE,
};
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
void _generate_meshes(Node *p_node, bool p_generate_lods, bool p_create_shadow_meshes);
void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<uint8_t> &r_dst_lightmap_cache);
public:
static ResourceImporterScene *get_singleton() { return singleton; }
@ -141,26 +139,39 @@ public:
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
enum InternalImportCategory {
INTERNAL_IMPORT_CATEGORY_NODE,
INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
INTERNAL_IMPORT_CATEGORY_MESH,
INTERNAL_IMPORT_CATEGORY_MATERIAL,
INTERNAL_IMPORT_CATEGORY_ANIMATION,
INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
INTERNAL_IMPORT_CATEGORY_MAX
};
void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
bool get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
virtual int get_import_order() const override { return 100; } //after everything
void _find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes);
Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map);
Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes);
Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode);
void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all);
void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep);
void _filter_tracks(Node *scene, const String &p_text);
void _optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
void _optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
Node *pre_import(const String &p_source_file);
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
virtual bool has_advanced_options() const override;
virtual void show_advanced_options(const String &p_path) override;
ResourceImporterScene();
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,199 @@
/*************************************************************************/
/* scene_import_settings.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef SCENEIMPORTSETTINGS_H
#define SCENEIMPORTSETTINGS_H
#include "editor/editor_file_dialog.h"
#include "editor/editor_inspector.h"
#include "editor/import/resource_importer_scene.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/split_container.h"
#include "scene/gui/subviewport_container.h"
#include "scene/gui/tab_container.h"
#include "scene/gui/tree.h"
#include "scene/resources/primitive_meshes.h"
class SceneImportSettingsData;
class SceneImportSettings : public ConfirmationDialog {
GDCLASS(SceneImportSettings, ConfirmationDialog)
static SceneImportSettings *singleton;
enum Actions {
ACTION_EXTRACT_MATERIALS,
ACTION_CHOOSE_MESH_SAVE_PATHS,
ACTION_CHOOSE_ANIMATION_SAVE_PATHS,
};
Node *scene = nullptr;
HSplitContainer *tree_split;
HSplitContainer *property_split;
TabContainer *data_mode;
Tree *scene_tree;
Tree *mesh_tree;
Tree *material_tree;
EditorInspector *inspector;
SubViewport *base_viewport;
Camera3D *camera;
bool first_aabb = false;
AABB contents_aabb;
DirectionalLight3D *light;
Ref<ArrayMesh> selection_mesh;
MeshInstance3D *node_selected;
MeshInstance3D *mesh_preview;
Ref<SphereMesh> material_preview;
float cam_rot_x;
float cam_rot_y;
float cam_zoom;
void _update_scene();
struct MaterialData {
bool has_import_id;
Ref<Material> material;
TreeItem *scene_node;
TreeItem *mesh_node;
TreeItem *material_node;
float cam_rot_x = -Math_PI / 4;
float cam_rot_y = -Math_PI / 4;
float cam_zoom = 1;
Map<StringName, Variant> settings;
};
Map<String, MaterialData> material_map;
struct MeshData {
bool has_import_id;
Ref<Mesh> mesh;
TreeItem *scene_node;
TreeItem *mesh_node;
float cam_rot_x = -Math_PI / 4;
float cam_rot_y = -Math_PI / 4;
float cam_zoom = 1;
Map<StringName, Variant> settings;
};
Map<String, MeshData> mesh_map;
struct AnimationData {
Ref<Animation> animation;
TreeItem *scene_node;
Map<StringName, Variant> settings;
};
Map<String, AnimationData> animation_map;
struct NodeData {
Node *node;
TreeItem *scene_node;
Map<StringName, Variant> settings;
};
Map<String, NodeData> node_map;
void _fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent);
void _fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent);
void _fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent);
void _fill_scene(Node *p_node, TreeItem *p_parent_item);
Set<Ref<Mesh>> mesh_set;
Set<Ref<Material>> material_set;
String selected_type;
String selected_id;
bool selecting = false;
void _update_camera();
void _select(Tree *p_from, String p_type, String p_id);
void _material_tree_selected();
void _mesh_tree_selected();
void _scene_tree_selected();
void _viewport_input(const Ref<InputEvent> &p_input);
Map<StringName, Variant> defaults;
SceneImportSettingsData *scene_import_settings_data;
void _re_import();
String base_path;
MenuButton *action_menu;
ConfirmationDialog *external_paths;
Tree *external_path_tree;
EditorFileDialog *save_path;
OptionButton *external_extension_type;
EditorFileDialog *item_save_path;
void _menu_callback(int p_id);
void _save_dir_callback(const String &p_path);
int current_action;
Vector<TreeItem *> save_path_items;
TreeItem *save_path_item = nullptr;
void _save_path_changed(const String &p_path);
void _browse_save_callback(Object *p_item, int p_column, int p_id);
void _save_dir_confirm();
Dictionary base_subresource_settings;
void _load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category);
protected:
void _notification(int p_what);
public:
void open_settings(const String &p_path);
static SceneImportSettings *get_singleton();
SceneImportSettings();
~SceneImportSettings();
};
#endif // SCENEIMPORTSETTINGS_H

View file

@ -136,6 +136,11 @@ Ref<Material> EditorSceneImporterMesh::get_surface_material(int p_surface) const
return surfaces[p_surface].material;
}
void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) {
ERR_FAIL_INDEX(p_surface, surfaces.size());
surfaces.write[p_surface].material = p_material;
}
void EditorSceneImporterMesh::generate_lods() {
if (!SurfaceTool::simplify_func) {
return;
@ -219,11 +224,20 @@ bool EditorSceneImporterMesh::has_mesh() const {
return mesh.is_valid();
}
Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh() {
Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<Mesh> &p_base) {
ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
if (mesh.is_null()) {
mesh.instance();
if (p_base.is_valid()) {
mesh = p_base;
}
if (mesh.is_null()) {
mesh.instance();
}
mesh->set_name(get_name());
if (has_meta("import_id")) {
mesh->set_meta("import_id", get_meta("import_id"));
}
for (int i = 0; i < blend_shapes.size(); i++) {
mesh->add_blend_shape(blend_shapes[i]);
}
@ -251,6 +265,8 @@ Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh() {
}
}
mesh->set_lightmap_size_hint(lightmap_size_hint);
if (shadow_mesh.is_valid()) {
Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
mesh->set_shadow_mesh(shadow);
@ -436,6 +452,338 @@ Dictionary EditorSceneImporterMesh::_get_data() const {
return data;
}
Vector<Face3> EditorSceneImporterMesh::get_faces() const {
Vector<Face3> faces;
for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];
if (indices.size()) {
for (int j = 0; j < indices.size(); j += 3) {
Face3 f;
f.vertex[0] = vertices[indices[j + 0]];
f.vertex[1] = vertices[indices[j + 1]];
f.vertex[2] = vertices[indices[j + 2]];
faces.push_back(f);
}
} else {
for (int j = 0; j < vertices.size(); j += 3) {
Face3 f;
f.vertex[0] = vertices[j + 0];
f.vertex[1] = vertices[j + 1];
f.vertex[2] = vertices[j + 2];
faces.push_back(f);
}
}
}
}
return faces;
}
Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose() const {
ERR_FAIL_COND_V(!Mesh::convex_composition_function, Vector<Ref<Shape3D>>());
const Vector<Face3> faces = get_faces();
Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces);
Vector<Ref<Shape3D>> ret;
for (int i = 0; i < decomposed.size(); i++) {
Set<Vector3> points;
for (int j = 0; j < decomposed[i].size(); j++) {
points.insert(decomposed[i][j].vertex[0]);
points.insert(decomposed[i][j].vertex[1]);
points.insert(decomposed[i][j].vertex[2]);
}
Vector<Vector3> convex_points;
convex_points.resize(points.size());
{
Vector3 *w = convex_points.ptrw();
int idx = 0;
for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) {
w[idx++] = E->get();
}
}
Ref<ConvexPolygonShape3D> shape;
shape.instance();
shape->set_points(convex_points);
ret.push_back(shape);
}
return ret;
}
Ref<Shape3D> EditorSceneImporterMesh::create_trimesh_shape() const {
Vector<Face3> faces = get_faces();
if (faces.size() == 0) {
return Ref<Shape3D>();
}
Vector<Vector3> face_points;
face_points.resize(faces.size() * 3);
for (int i = 0; i < face_points.size(); i += 3) {
Face3 f = faces.get(i / 3);
face_points.set(i, f.vertex[0]);
face_points.set(i + 1, f.vertex[1]);
face_points.set(i + 2, f.vertex[2]);
}
Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
shape->set_faces(face_points);
return shape;
}
Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
Vector<Face3> faces = get_faces();
if (faces.size() == 0) {
return Ref<NavigationMesh>();
}
Map<Vector3, int> unique_vertices;
LocalVector<int> face_indices;
for (int i = 0; i < faces.size(); i++) {
for (int j = 0; j < 3; j++) {
Vector3 v = faces[i].vertex[j];
int idx;
if (unique_vertices.has(v)) {
idx = unique_vertices[v];
} else {
idx = unique_vertices.size();
unique_vertices[v] = idx;
}
face_indices.push_back(idx);
}
}
Vector<Vector3> vertices;
vertices.resize(unique_vertices.size());
for (Map<Vector3, int>::Element *E = unique_vertices.front(); E; E = E->next()) {
vertices.write[E->get()] = E->key();
}
Ref<NavigationMesh> nm;
nm.instance();
nm->set_vertices(vertices);
Vector<int> v3;
v3.resize(3);
for (uint32_t i = 0; i < face_indices.size(); i += 3) {
v3.write[0] = face_indices[i + 0];
v3.write[1] = face_indices[i + 1];
v3.write[2] = face_indices[i + 2];
nm->add_polygon(v3);
}
return nm;
}
extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
struct EditorSceneImporterMeshLightmapSurface {
Ref<Material> material;
LocalVector<SurfaceTool::Vertex> vertices;
Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
uint32_t format = 0;
String name;
};
Error EditorSceneImporterMesh::lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size) {
ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
Vector<float> vertices;
Vector<float> normals;
Vector<int> indices;
Vector<float> uv;
Vector<Pair<int, int>> uv_indices;
Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
// Keep only the scale
Transform transform = p_base_transform;
transform.origin = Vector3();
transform.looking_at(Vector3(1, 0, 0), Vector3(0, 1, 0));
Basis normal_basis = transform.basis.inverse().transposed();
for (int i = 0; i < get_surface_count(); i++) {
EditorSceneImporterMeshLightmapSurface s;
s.primitive = get_surface_primitive_type(i);
ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap.");
Array arrays = get_surface_arrays(i);
s.material = get_surface_material(i);
s.name = get_surface_name(i);
SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
Vector<Vector3> rvertices = arrays[Mesh::ARRAY_VERTEX];
int vc = rvertices.size();
const Vector3 *r = rvertices.ptr();
Vector<Vector3> rnormals = arrays[Mesh::ARRAY_NORMAL];
ERR_FAIL_COND_V_MSG(rnormals.size() == 0, ERR_UNAVAILABLE, "Normals are required for lightmap unwrap.");
const Vector3 *rn = rnormals.ptr();
int vertex_ofs = vertices.size() / 3;
vertices.resize((vertex_ofs + vc) * 3);
normals.resize((vertex_ofs + vc) * 3);
uv_indices.resize(vertex_ofs + vc);
for (int j = 0; j < vc; j++) {
Vector3 v = transform.xform(r[j]);
Vector3 n = normal_basis.xform(rn[j]).normalized();
vertices.write[(j + vertex_ofs) * 3 + 0] = v.x;
vertices.write[(j + vertex_ofs) * 3 + 1] = v.y;
vertices.write[(j + vertex_ofs) * 3 + 2] = v.z;
normals.write[(j + vertex_ofs) * 3 + 0] = n.x;
normals.write[(j + vertex_ofs) * 3 + 1] = n.y;
normals.write[(j + vertex_ofs) * 3 + 2] = n.z;
uv_indices.write[j + vertex_ofs] = Pair<int, int>(i, j);
}
Vector<int> rindices = arrays[Mesh::ARRAY_INDEX];
int ic = rindices.size();
if (ic == 0) {
for (int j = 0; j < vc / 3; j++) {
if (Face3(r[j * 3 + 0], r[j * 3 + 1], r[j * 3 + 2]).is_degenerate()) {
continue;
}
indices.push_back(vertex_ofs + j * 3 + 0);
indices.push_back(vertex_ofs + j * 3 + 1);
indices.push_back(vertex_ofs + j * 3 + 2);
}
} else {
const int *ri = rindices.ptr();
for (int j = 0; j < ic / 3; j++) {
if (Face3(r[ri[j * 3 + 0]], r[ri[j * 3 + 1]], r[ri[j * 3 + 2]]).is_degenerate()) {
continue;
}
indices.push_back(vertex_ofs + ri[j * 3 + 0]);
indices.push_back(vertex_ofs + ri[j * 3 + 1]);
indices.push_back(vertex_ofs + ri[j * 3 + 2]);
}
}
lightmap_surfaces.push_back(s);
}
//unwrap
float *gen_uvs;
int *gen_vertices;
int *gen_indices;
int gen_vertex_count;
int gen_index_count;
int size_x;
int size_y;
bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y, r_cache_data, r_cache_size, r_used_cache);
if (!ok) {
return ERR_CANT_CREATE;
}
//remove surfaces
clear();
//create surfacetools for each surface..
Vector<Ref<SurfaceTool>> surfaces_tools;
for (int i = 0; i < lightmap_surfaces.size(); i++) {
Ref<SurfaceTool> st;
st.instance();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
st->set_material(lightmap_surfaces[i].material);
st->set_meta("name", lightmap_surfaces[i].name);
surfaces_tools.push_back(st); //stay there
}
print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
//go through all indices
for (int i = 0; i < gen_index_count; i += 3) {
ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], uv_indices.size(), ERR_BUG);
ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], uv_indices.size(), ERR_BUG);
ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], uv_indices.size(), ERR_BUG);
ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first;
for (int j = 0; j < 3; j++) {
SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
surfaces_tools.write[surface]->set_color(v.color);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
surfaces_tools.write[surface]->set_uv(v.uv);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
surfaces_tools.write[surface]->set_normal(v.normal);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
Plane t;
t.normal = v.tangent;
t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
surfaces_tools.write[surface]->set_tangent(t);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
surfaces_tools.write[surface]->set_bones(v.bones);
}
if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
surfaces_tools.write[surface]->set_weights(v.weights);
}
Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
surfaces_tools.write[surface]->set_uv2(uv2);
surfaces_tools.write[surface]->add_vertex(v.vertex);
}
}
//generate surfaces
for (int i = 0; i < surfaces_tools.size(); i++) {
surfaces_tools.write[i]->index();
Array arrays = surfaces_tools.write[i]->commit_to_arrays();
add_surface(surfaces_tools.write[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools.write[i]->get_material(), surfaces_tools.write[i]->get_meta("name"));
}
set_lightmap_size_hint(Size2(size_x, size_y));
if (!r_used_cache) {
//free stuff
::free(gen_vertices);
::free(gen_indices);
::free(gen_uvs);
}
return OK;
}
void EditorSceneImporterMesh::set_lightmap_size_hint(const Size2i &p_size) {
lightmap_size_hint = p_size;
}
Size2i EditorSceneImporterMesh::get_lightmap_size_hint() const {
return lightmap_size_hint;
}
void EditorSceneImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &EditorSceneImporterMesh::add_blend_shape);
ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &EditorSceneImporterMesh::get_blend_shape_count);
@ -462,5 +810,8 @@ void EditorSceneImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data", "data"), &EditorSceneImporterMesh::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &EditorSceneImporterMesh::_get_data);
ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &EditorSceneImporterMesh::set_lightmap_size_hint);
ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &EditorSceneImporterMesh::get_lightmap_size_hint);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
}

View file

@ -32,7 +32,10 @@
#define EDITOR_SCENE_IMPORTER_MESH_H
#include "core/io/resource.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h"
// The following classes are used by importers instead of ArrayMesh and MeshInstance3D
// so the data is not registered (hence, quality loss), importing happens faster and
// its easier to modify before saving
@ -63,6 +66,8 @@ class EditorSceneImporterMesh : public Resource {
Ref<EditorSceneImporterMesh> shadow_mesh;
Size2i lightmap_size_hint;
protected:
void _set_data(const Dictionary &p_data);
Dictionary _get_data() const;
@ -89,13 +94,24 @@ public:
float get_surface_lod_size(int p_surface, int p_lod) const;
Ref<Material> get_surface_material(int p_surface) const;
void set_surface_material(int p_surface, const Ref<Material> &p_material);
void generate_lods();
void create_shadow_mesh();
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
Vector<Face3> get_faces() const;
Vector<Ref<Shape3D>> convex_decompose() const;
Ref<Shape3D> create_trimesh_shape() const;
Ref<NavigationMesh> create_navigation_mesh();
Error lightmap_unwrap_cached(int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache, const Transform &p_base_transform, float p_texel_size);
void set_lightmap_size_hint(const Size2i &p_size);
Size2i get_lightmap_size_hint() const;
bool has_mesh() const;
Ref<ArrayMesh> get_mesh();
Ref<ArrayMesh> get_mesh(const Ref<Mesh> &p_base = Ref<Mesh>());
void clear();
};
#endif // EDITOR_SCENE_IMPORTER_MESH_H

View file

@ -156,6 +156,14 @@ void ImportDock::_update_options(const Ref<ConfigFile> &p_config) {
params->update();
_update_preset_menu();
if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
advanced->show();
advanced_spacer->show();
} else {
advanced->hide();
advanced_spacer->hide();
}
}
void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
@ -258,6 +266,14 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
preset->set_disabled(false);
imported->set_text(vformat(TTR("%d Files"), p_paths.size()));
if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
advanced->show();
advanced_spacer->show();
} else {
advanced->hide();
advanced_spacer->hide();
}
}
void ImportDock::_update_preset_menu() {
@ -422,6 +438,11 @@ void ImportDock::_reimport_and_restart() {
EditorNode::get_singleton()->restart_editor();
}
void ImportDock::_advanced_options() {
if (params->paths.size() == 1 && params->importer.is_valid()) {
params->importer->show_advanced_options(params->paths[0]);
}
}
void ImportDock::_reimport() {
for (int i = 0; i < params->paths.size(); i++) {
Ref<ConfigFile> config;
@ -531,10 +552,27 @@ ImportDock::ImportDock() {
import->set_text(TTR("Reimport"));
import->set_disabled(true);
import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt));
if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) {
advanced_spacer = hb->add_spacer();
advanced = memnew(Button);
advanced->set_text(TTR("Advanced..."));
hb->add_child(advanced);
}
hb->add_spacer();
hb->add_child(import);
hb->add_spacer();
if (DisplayServer::get_singleton()->get_swap_cancel_ok()) {
advanced = memnew(Button);
advanced->set_text(TTR("Advanced..."));
hb->add_child(advanced);
advanced_spacer = hb->add_spacer();
}
advanced->hide();
advanced_spacer->hide();
advanced->connect("pressed", callable_mp(this, &ImportDock::_advanced_options));
reimport_confirm = memnew(ConfirmationDialog);
reimport_confirm->get_ok_button()->set_text(TTR("Save Scenes, Re-Import, and Restart"));
add_child(reimport_confirm);

View file

@ -57,6 +57,9 @@ class ImportDock : public VBoxContainer {
Label *label_warning;
Button *import;
Control *advanced_spacer;
Button *advanced;
ImportDockParameters *params;
void _preset_selected(int p_idx);
@ -69,6 +72,7 @@ class ImportDock : public VBoxContainer {
void _reimport_and_restart();
void _reimport();
void _advanced_options();
enum {
ITEM_SET_AS_DEFAULT = 100,
ITEM_LOAD_DEFAULT,

View file

@ -628,7 +628,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
mesh_data_precached->mesh_node = fbx_node;
// mesh node, mesh id
mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, (p_flags & IMPORT_USE_COMPRESSION) != 0);
mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, 0);
if (!state.MeshNodes.has(mesh_id)) {
state.MeshNodes.insert(mesh_id, fbx_node);
}

View file

@ -99,7 +99,9 @@ Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags,
Ref<GLTFDocument> gltf_document;
gltf_document.instance();
Error err = gltf_document->parse(r_state, p_path);
*r_err = err;
if (r_err) {
*r_err = err;
}
ERR_FAIL_COND_V(err != Error::OK, nullptr);
Node3D *root = memnew(Node3D);

View file

@ -313,7 +313,7 @@ BoxContainer::AlignMode BoxContainer::get_alignment() const {
return align;
}
void BoxContainer::add_spacer(bool p_begin) {
Control *BoxContainer::add_spacer(bool p_begin) {
Control *c = memnew(Control);
c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
@ -327,6 +327,8 @@ void BoxContainer::add_spacer(bool p_begin) {
if (p_begin) {
move_child(c, 0);
}
return c;
}
BoxContainer::BoxContainer(bool p_vertical) {

View file

@ -55,7 +55,7 @@ protected:
static void _bind_methods();
public:
void add_spacer(bool p_begin = false);
Control *add_spacer(bool p_begin = false);
void set_alignment(AlignMode p_align);
AlignMode get_alignment() const;

View file

@ -410,6 +410,14 @@ bool TreeItem::is_collapsed() {
return collapsed;
}
void TreeItem::uncollapse_tree() {
TreeItem *t = this;
while (t) {
t->set_collapsed(false);
t = t->parent;
}
}
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
_changed_notify();
@ -842,6 +850,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
ClassDB::bind_method(D_METHOD("uncollapse_tree"), &TreeItem::uncollapse_tree);
ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height);
ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height);

View file

@ -229,6 +229,8 @@ public:
void set_collapsed(bool p_collapsed);
bool is_collapsed();
void uncollapse_tree();
void set_custom_minimum_height(int p_height);
int get_custom_minimum_height() const;

View file

@ -893,12 +893,13 @@ void Window::_window_input(const Ref<InputEvent> &p_ev) {
}
if (exclusive_child != nullptr) {
/*
Window *focus_target = exclusive_child;
focus_target->grab_focus();
while (focus_target->exclusive_child != nullptr) {
focus_target = focus_target->exclusive_child;
focus_target->grab_focus();
}
}*/
if (!is_embedding_subwindows()) { //not embedding, no need for event
return;

View file

@ -1059,6 +1059,10 @@ void SurfaceTool::set_material(const Ref<Material> &p_material) {
material = p_material;
}
Ref<Material> SurfaceTool::get_material() const {
return material;
}
void SurfaceTool::clear() {
begun = false;
primitive = Mesh::PRIMITIVE_LINES;
@ -1088,6 +1092,10 @@ void SurfaceTool::set_custom_format(int p_index, CustomFormat p_format) {
ERR_FAIL_COND(begun);
last_custom_format[p_index] = p_format;
}
Mesh::PrimitiveType SurfaceTool::get_primitive() const {
return primitive;
}
SurfaceTool::CustomFormat SurfaceTool::get_custom_format(int p_index) const {
ERR_FAIL_INDEX_V(p_index, RS::ARRAY_CUSTOM_COUNT, CUSTOM_MAX);
return last_custom_format[p_index];
@ -1174,6 +1182,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_lod", "nd_threshold", "target_index_count"), &SurfaceTool::generate_lod, DEFVAL(3));
ClassDB::bind_method(D_METHOD("set_material", "material"), &SurfaceTool::set_material);
ClassDB::bind_method(D_METHOD("get_primitive"), &SurfaceTool::get_primitive);
ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear);

View file

@ -143,6 +143,8 @@ public:
void set_custom_format(int p_index, CustomFormat p_format);
CustomFormat get_custom_format(int p_index) const;
Mesh::PrimitiveType get_primitive() const;
void begin(Mesh::PrimitiveType p_primitive);
void set_color(Color p_color);
@ -171,6 +173,7 @@ public:
Vector<int> generate_lod(float p_threshold, int p_target_index_count = 3);
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
void clear();