Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new scene import option to import as Skeleton #88819

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/classes/ResourceImporterScene.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
<member name="nodes/apply_root_scale" type="bool" setter="" getter="" default="true">
If [code]true[/code], [member nodes/root_scale] will be applied to the descendant nodes, meshes, animations, bones, etc. This means that if you add a child node later on within the imported scene, it won't be scaled. If [code]false[/code], [member nodes/root_scale] will multiply the scale of the root node instead.
</member>
<member name="nodes/import_as_skeleton_bones" type="bool" setter="" getter="" default="false">
Treat all nodes in the imported scene as if they are bones within a single [Skeleton3D]. Can be used to guarantee that imported animations target skeleton bones rather than nodes. May also be used to assign the [code]"Root"[/code] bone in a [BoneMap]. See [url=$DOCS_URL/tutorials/assets_pipeline/retargeting_3d_skeletons.html]Retargeting 3D Skeletons[/url] for more information.
</member>
<member name="nodes/root_name" type="String" setter="" getter="" default="&quot;&quot;">
Override for the root node name. If empty, the root node will use what the scene specifies, or the file name if the scene does not specify a root name.
</member>
Expand Down
1 change: 1 addition & 0 deletions editor/import/3d/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import

r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/import_as_skeleton_bones"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
Expand Down
16 changes: 16 additions & 0 deletions editor/import/3d/scene_import_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,16 @@ void SceneImportSettingsDialog::_update_view_gizmos() {
if (!is_visible()) {
return;
}
const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current;
if (main_settings.has("nodes/import_as_skeleton_bones")) {
bool new_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"];
if (new_import_as_skeleton != previous_import_as_skeleton) {
previous_import_as_skeleton = new_import_as_skeleton;
_re_import();
open_settings(base_path);
}
return;
}
for (const KeyValue<String, NodeData> &e : node_map) {
bool show_collider_view = false;
if (e.value.settings.has(SNAME("generate/physics"))) {
Expand Down Expand Up @@ -591,6 +601,7 @@ void SceneImportSettingsDialog::update_view() {

void SceneImportSettingsDialog::open_settings(const String &p_path, bool p_for_animation) {
if (scene) {
_cleanup();
memdelete(scene);
scene = nullptr;
}
Expand Down Expand Up @@ -667,6 +678,10 @@ void SceneImportSettingsDialog::open_settings(const String &p_path, bool p_for_a
first_aabb = false;
}

const HashMap<StringName, Variant> &main_settings = scene_import_settings_data->current;
if (main_settings.has("nodes/import_as_skeleton_bones")) {
previous_import_as_skeleton = main_settings["nodes/import_as_skeleton_bones"];
}
popup_centered_ratio();
_update_view_gizmos();
_update_camera();
Expand Down Expand Up @@ -1137,6 +1152,7 @@ void SceneImportSettingsDialog::_re_import() {
main_settings["_subresources"] = subresources;
}

_cleanup(); // Prevent skeletons and other pointers from pointing to dangling references.
EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, editing_animation ? "animation_library" : "scene", main_settings);
}

Expand Down
1 change: 1 addition & 0 deletions editor/import/3d/scene_import_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
Button *animation_stop_button = nullptr;
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
bool animation_pingpong = false;
bool previous_import_as_skeleton = false;

Ref<StandardMaterial3D> collider_mat;

Expand Down
3 changes: 3 additions & 0 deletions modules/fbx/editor/editor_scene_importer_fbx2gltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ Node *EditorSceneFormatImporterFBX2GLTF::import_scene(const String &p_path, uint
gltf.instantiate();
Ref<GLTFState> state;
state.instantiate();
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
print_verbose(vformat("glTF path: %s", sink));
Error err = gltf->append_from_file(sink, state, p_flags, p_path.get_base_dir());
if (err != OK) {
Expand Down
6 changes: 6 additions & 0 deletions modules/fbx/editor/editor_scene_importer_ufbx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
int32_t enum_option = p_options["fbx/embedded_image_handling"];
state->set_handle_binary_image(enum_option);
}
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir());
if (err != OK) {
Expand Down
2 changes: 1 addition & 1 deletion modules/fbx/fbx_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,7 @@ Error FBXDocument::_parse_fbx_state(Ref<FBXState> p_state, const String &p_searc
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);

/* DETERMINE SKELETONS */
err = SkinTool::_determine_skeletons(p_state->skins, p_state->nodes, p_state->skeletons);
err = SkinTool::_determine_skeletons(p_state->skins, p_state->nodes, p_state->skeletons, p_state->get_import_as_skeleton_bones() ? p_state->root_nodes : Vector<GLTFNodeIndex>());
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);

/* CREATE SKELETONS */
Expand Down
5 changes: 5 additions & 0 deletions modules/gltf/doc_classes/GLTFState.xml
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,13 @@
The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
The binary buffer attached to a .glb file.
</member>
<member name="import_as_skeleton_bones" type="bool" setter="set_import_as_skeleton_bones" getter="get_import_as_skeleton_bones" default="false">
True to force all GLTFNodes in the document to be bones of a single Skeleton3D godot node.
</member>
<member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}">
The original raw JSON document corresponding to this GLTFState.
</member>
<member name="major_version" type="int" setter="set_major_version" getter="get_major_version" default="0">
</member>
Expand Down
3 changes: 3 additions & 0 deletions modules/gltf/editor/editor_scene_importer_blend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) {
base_dir = sink.get_base_dir();
}
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
state->set_scene_name(blend_basename);
err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir);
if (err != OK) {
Expand Down
2 changes: 1 addition & 1 deletion modules/gltf/gltf_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6924,7 +6924,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);

/* DETERMINE SKELETONS */
err = SkinTool::_determine_skeletons(p_state->skins, p_state->nodes, p_state->skeletons);
err = SkinTool::_determine_skeletons(p_state->skins, p_state->nodes, p_state->skeletons, p_state->get_import_as_skeleton_bones() ? p_state->root_nodes : Vector<GLTFNodeIndex>());
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);

/* PARSE MESHES (we have enough info now) */
Expand Down
11 changes: 11 additions & 0 deletions modules/gltf/gltf_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skeletons", "skeletons"), &GLTFState::set_skeletons);
ClassDB::bind_method(D_METHOD("get_create_animations"), &GLTFState::get_create_animations);
ClassDB::bind_method(D_METHOD("set_create_animations", "create_animations"), &GLTFState::set_create_animations);
ClassDB::bind_method(D_METHOD("get_import_as_skeleton_bones"), &GLTFState::get_import_as_skeleton_bones);
ClassDB::bind_method(D_METHOD("set_import_as_skeleton_bones", "import_as_skeleton_bones"), &GLTFState::set_import_as_skeleton_bones);
ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations);
ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations);
ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node);
Expand Down Expand Up @@ -125,6 +127,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set<String>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>>
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_animations"), "set_create_animations", "get_create_animations"); // bool
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "import_as_skeleton_bones"), "set_import_as_skeleton_bones", "get_import_as_skeleton_bones"); // bool
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum

Expand Down Expand Up @@ -337,6 +340,14 @@ void GLTFState::set_create_animations(bool p_create_animations) {
create_animations = p_create_animations;
}

bool GLTFState::get_import_as_skeleton_bones() {
return import_as_skeleton_bones;
}

void GLTFState::set_import_as_skeleton_bones(bool p_import_as_skeleton_bones) {
import_as_skeleton_bones = p_import_as_skeleton_bones;
}

TypedArray<GLTFAnimation> GLTFState::get_animations() {
return GLTFTemplateConvert::to_array(animations);
}
Expand Down
4 changes: 4 additions & 0 deletions modules/gltf/gltf_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class GLTFState : public Resource {
bool force_generate_tangents = false;
bool create_animations = true;
bool force_disable_compression = false;
bool import_as_skeleton_bones = false;

int handle_binary_image = HANDLE_BINARY_EXTRACT_TEXTURES;

Expand Down Expand Up @@ -213,6 +214,9 @@ class GLTFState : public Resource {
bool get_create_animations();
void set_create_animations(bool p_create_animations);

bool get_import_as_skeleton_bones();
void set_import_as_skeleton_bones(bool p_import_as_skeleton_bones);

TypedArray<GLTFAnimation> get_animations();
void set_animations(TypedArray<GLTFAnimation> p_animations);

Expand Down
13 changes: 12 additions & 1 deletion modules/gltf/skin_tool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,18 @@ void SkinTool::_recurse_children(
Error SkinTool::_determine_skeletons(
Vector<Ref<GLTFSkin>> &skins,
Vector<Ref<GLTFNode>> &nodes,
Vector<Ref<GLTFSkeleton>> &skeletons) {
Vector<Ref<GLTFSkeleton>> &skeletons,
const Vector<GLTFNodeIndex> &p_single_skeleton_roots) {
if (!p_single_skeleton_roots.is_empty()) {
Ref<GLTFSkin> skin;
skin.instantiate();
skin->set_name("godot_single_skeleton_root");
for (GLTFNodeIndex i = 0; i < p_single_skeleton_roots.size(); i++) {
skin->joints.push_back(p_single_skeleton_roots[i]);
}
skins.push_back(skin);
}

// Using a disjoint set, we are going to potentially combine all skins that are actually branches
// of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton.
// This is another unclear issue caused by the current glTF specification.
Expand Down
3 changes: 2 additions & 1 deletion modules/gltf/skin_tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ class SkinTool {
static Error _determine_skeletons(
Vector<Ref<GLTFSkin>> &r_skins,
Vector<Ref<GLTFNode>> &r_nodes,
Vector<Ref<GLTFSkeleton>> &r_skeletons);
Vector<Ref<GLTFSkeleton>> &r_skeletons,
const Vector<GLTFNodeIndex> &p_single_skeleton_roots);
static Error _create_skeletons(
HashSet<String> &r_unique_names,
Vector<Ref<GLTFSkin>> &r_skins,
Expand Down
Loading