Skip to content

Commit

Permalink
Add ufbx for FBX importing
Browse files Browse the repository at this point in the history
This update introduces a new import method for FBX files using ufbx. If the fbx2gltf import fails, it will use the most recently cached scene from the ufbx import. The process is sped up by introducing threads to load the ufbx portion.

Key changes include:

- Support for importing geometry helper nodes in FBX files.
- Addition of cameras and lights with updated names.
- Removal of the fbx importer manager.
- Introduction of ModelDocument3D and updates to its methods.
- Changes to FBX import options and visibility.
- Updating the documentation and handling some errors.
- Store the original non-unique node, mesh and animation names in FBX and glTF.

Co-Authored-By: bqqbarbhg <bqqbarbhg@gmail.com>
  • Loading branch information
2 people authored and akien-mga committed Feb 23, 2024
1 parent 2fe8f07 commit 04d4394
Show file tree
Hide file tree
Showing 63 changed files with 41,640 additions and 910 deletions.
5 changes: 5 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,11 @@ Copyright: 2014-2021, Syoyo Fujita
2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC
License: BSD-3-clause

Files: ./thirdparty/ufbx/
Comment: ufbx
Copyright: 2020, Samuli Raivio
License: Expat

Files: ./thirdparty/vhacd/
Comment: V-HACD
Copyright: 2011, Khaled Mamou
Expand Down
4 changes: 2 additions & 2 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,9 @@
The maximum idle uptime (in seconds) of the Blender process.
This prevents Godot from having to create a new process for each import within the given seconds.
</member>
<member name="filesystem/import/fbx/fbx2gltf_path" type="String" setter="" getter="">
<member name="filesystem/import/fbx2gltf/fbx2gltf_path" type="String" setter="" getter="">
The path to the FBX2glTF executable used for converting Autodesk FBX 3D scene files [code].fbx[/code] to glTF 2.0 format during import.
To enable this feature for your specific project, use [member ProjectSettings.filesystem/import/fbx/enabled].
To enable this feature for your specific project, use [member ProjectSettings.filesystem/import/fbx2gltf/enabled].
</member>
<member name="filesystem/on_save/compress_binary_resources" type="bool" setter="" getter="">
If [code]true[/code], uses lossless compression for binary resources.
Expand Down
12 changes: 6 additions & 6 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -984,15 +984,15 @@
<member name="filesystem/import/blender/enabled.web" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/blender/enabled] on the Web where Blender can't easily be accessed from Godot.
</member>
<member name="filesystem/import/fbx/enabled" type="bool" setter="" getter="" default="true">
<member name="filesystem/import/fbx2gltf/enabled" type="bool" setter="" getter="" default="true">
If [code]true[/code], Autodesk FBX 3D scene files with the [code].fbx[/code] extension will be imported by converting them to glTF 2.0.
This requires configuring a path to a FBX2glTF executable in the editor settings at [code]filesystem/import/fbx/fbx2gltf_path[/code].
This requires configuring a path to a FBX2glTF executable in the editor settings at [member EditorSettings.filesystem/import/fbx2gltf/fbx2gltf_path].
</member>
<member name="filesystem/import/fbx/enabled.android" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/fbx/enabled] on Android where FBX2glTF can't easily be accessed from Godot.
<member name="filesystem/import/fbx2gltf/enabled.android" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/fbx2gltf/enabled] on Android where FBX2glTF can't easily be accessed from Godot.
</member>
<member name="filesystem/import/fbx/enabled.web" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/fbx/enabled] on the Web where FBX2glTF can't easily be accessed from Godot.
<member name="filesystem/import/fbx2gltf/enabled.web" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/fbx2gltf/enabled] on the Web where FBX2glTF can't easily be accessed from Godot.
</member>
<member name="gui/common/default_scroll_deadzone" type="int" setter="" getter="" default="0">
Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden.
Expand Down
2 changes: 1 addition & 1 deletion editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/blender/blender_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_port", 6011, "0,65535,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_server_uptime", 5, "0,300,1,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/fbx/fbx2gltf_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/fbx2gltf/fbx2gltf_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)

// Tools (denoise)
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/tools/oidn/oidn_denoise_path", "", "", PROPERTY_USAGE_DEFAULT)
Expand Down
12 changes: 6 additions & 6 deletions editor/fbx_importer_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void FBXImporterManager::_notification(int p_what) {
}

void FBXImporterManager::show_dialog(bool p_exclusive) {
String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path");
String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx2gltf/fbx2gltf_path");
fbx_path->set_text(fbx2gltf_path);
_validate_path(fbx2gltf_path);

Expand All @@ -56,8 +56,8 @@ void FBXImporterManager::show_dialog(bool p_exclusive) {
set_close_on_escape(!p_exclusive);

if (is_importing) {
get_cancel_button()->set_text(TTR("Disable FBX & Restart"));
get_cancel_button()->set_tooltip_text(TTR("Canceling this dialog will disable the FBX importer.\nYou can re-enable it in the Project Settings under Filesystem > Import > FBX > Enabled.\n\nThe editor will restart as importers are registered when the editor starts."));
get_cancel_button()->set_text(TTR("Disable FBX2glTF & Restart"));
get_cancel_button()->set_tooltip_text(TTR("Canceling this dialog will disable the FBX2glTF importer and use the ufbx importer.\nYou can re-enable FBX2glTF in the Project Settings under Filesystem > Import > FBX > Enabled.\n\nThe editor will restart as importers are registered when the editor starts."));
} else {
get_cancel_button()->set_text(TTR("Cancel"));
get_cancel_button()->set_tooltip_text("");
Expand Down Expand Up @@ -105,7 +105,7 @@ void FBXImporterManager::_select_file(const String &p_path) {

void FBXImporterManager::_path_confirmed() {
String path = fbx_path->get_text();
EditorSettings::get_singleton()->set("filesystem/import/fbx/fbx2gltf_path", path);
EditorSettings::get_singleton()->set("filesystem/import/fbx2gltf/fbx2gltf_path", path);
EditorSettings::get_singleton()->save();
}

Expand All @@ -114,7 +114,7 @@ void FBXImporterManager::_cancel_setup() {
return; // No worry.
}
// No escape.
ProjectSettings::get_singleton()->set("filesystem/import/fbx/enabled", false);
ProjectSettings::get_singleton()->set("filesystem/import/fbx2gltf/enabled", false);
ProjectSettings::get_singleton()->save();
EditorNode::get_singleton()->save_all_scenes();
EditorNode::get_singleton()->restart_editor();
Expand All @@ -136,7 +136,7 @@ FBXImporterManager::FBXImporterManager() {
set_title(TTR("Configure FBX Importer"));

VBoxContainer *vb = memnew(VBoxContainer);
vb->add_child(memnew(Label(TTR("FBX2glTF is required for importing FBX files.\nPlease download it and provide a valid path to the binary:"))));
vb->add_child(memnew(Label(TTR("FBX2glTF is required for importing FBX files if using FBX2glTF.\nAlternatively, you can use ufbx by disabling FBX2glTF.\nPlease download the necessary tool and provide a valid path to the binary:"))));
LinkButton *lb = memnew(LinkButton);
lb->set_text(TTR("Click this link to download FBX2glTF"));
lb->set_uri("https://godotengine.org/fbx-import");
Expand Down
29 changes: 17 additions & 12 deletions editor/import/3d/post_import_plugin_skeleton_renamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p
return;
}
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);

// Rename bones in Skeleton3D.
{
if (skeleton) {
// Rename bones in Skeleton3D.
int len = skeleton->get_bone_count();
for (int i = 0; i < len; i++) {
StringName bn = p_rename_map[skeleton->get_bone_name(i)];
if (bn) {
skeleton->set_bone_name(i, bn);
String current_bone_name = skeleton->get_bone_name(i);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);
if (new_bone_name) {
skeleton->set_bone_name(i, new_bone_name->value);
}
}
}
Expand All @@ -76,10 +76,13 @@ void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p
Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node);
if (mesh_skeleton && node == skeleton) {
int len = skin->get_bind_count();

for (int i = 0; i < len; i++) {
StringName bn = p_rename_map[skin->get_bind_name(i)];
if (bn) {
skin->set_bind_name(i, bn);
String current_bone_name = skin->get_bind_name(i);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);

if (new_bone_name) {
skin->set_bind_name(i, new_bone_name->value);
}
}
}
Expand Down Expand Up @@ -107,9 +110,11 @@ void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p
if (node) {
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
if (track_skeleton && track_skeleton == skeleton) {
StringName bn = p_rename_map[anim->track_get_path(i).get_subname(0)];
if (bn) {
anim->track_set_path(i, track_path + ":" + bn);
String current_bone_name = anim->track_get_path(i).get_subname(0);
const HashMap<String, String>::ConstIterator new_bone_name = p_rename_map.find(current_bone_name);
if (new_bone_name) {
String new_track_path = track_path + ":" + new_bone_name->value;
anim->track_set_path(i, new_track_path);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion editor/import/3d/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,9 @@ void _apply_scale_to_scalable_node_collection(ScalableNodeCollection &p_collecti
if (skeleton_3d) {
for (int i = 0; i < skeleton_3d->get_bone_count(); i++) {
Transform3D rest = skeleton_3d->get_bone_rest(i);
Vector3 position = skeleton_3d->get_bone_pose_position(i);
skeleton_3d->set_bone_rest(i, Transform3D(rest.basis, p_scale * rest.origin));
skeleton_3d->set_bone_pose_position(i, p_scale * rest.origin);
skeleton_3d->set_bone_pose_position(i, p_scale * position);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion editor/project_converter_3_to_4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,7 +1168,7 @@ bool ProjectConverter3To4::test_array_names() {

// Callable is special class, to which normal classes may be renamed.
if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {
ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", old_class));
ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", new_class));
valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
}
}
Expand Down
2 changes: 1 addition & 1 deletion editor/renames_map_3_to_4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1514,7 +1514,7 @@ const char *RenamesMap3To4::class_renames[][2] = {
{ "DynamicFontData", "FontFile" },
{ "EditorNavigationMeshGenerator", "NavigationMeshGenerator" },
{ "EditorSceneImporter", "EditorSceneFormatImporter" },
{ "EditorSceneImporterFBX", "EditorSceneFormatImporterFBX" },
{ "EditorSceneImporterFBX", "EditorSceneFormatImporterFBX2GLTF" },
{ "EditorSceneImporterGLTF", "EditorSceneFormatImporterGLTF" },
{ "EditorSpatialGizmo", "EditorNode3DGizmo" },
{ "EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin" },
Expand Down
11 changes: 11 additions & 0 deletions misc/extension_api_validation/4.2-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,14 @@ Validate extension JSON: Error: Field 'classes/Window/methods/has_theme_stylebox
Fix the default parameter value for StringName and Variant.
The changes to StringName parameters should be equivalent to the previous default values.
The change to the Variant parameter in 'add_code_completion_option' breaks behavior compatibility.


GH-81746
--------
Validate extension JSON: API was removed: classes/EditorSceneFormatImporterFBX

Renamed to EditorSceneFormatImporterFBX2GLTF.

The compat breakage was deemed necessary as this is a class most users wouldn't
use directly, and the name needs to be disambiguated with the new
EditorSceneFormatImporterUFBX.
48 changes: 48 additions & 0 deletions modules/fbx/SCsub
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python

Import("env")
Import("env_modules")

env_fbx = env_modules.Clone()

# Thirdparty source files

thirdparty_obj = []

thirdparty_dir = "#thirdparty/ufbx/"
thirdparty_sources = [thirdparty_dir + "ufbx.c"]

env_fbx.Prepend(CPPPATH=[thirdparty_dir])

env_thirdparty = env_fbx.Clone()
env_thirdparty.disable_warnings()

env_thirdparty.Append(
CPPDEFINES=[
"UFBX_NO_SUBDIVISION",
"UFBX_NO_TESSELLATION",
"UFBX_NO_GEOMETRY_CACHE",
"UFBX_NO_SCENE_EVALUATION",
"UFBX_NO_INDEX_GENERATION",
"UFBX_NO_SKINNING_EVALUATION",
"UFBX_NO_FORMAT_OBJ",
]
)

env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
env.modules_sources += thirdparty_obj

# Godot source files

module_obj = []

env_fbx.add_source_files(module_obj, "*.cpp")
env_fbx.add_source_files(module_obj, "structures/*.cpp")

if env.editor_build:
env_fbx.add_source_files(module_obj, "editor/*.cpp")

env.modules_sources += module_obj

# Needed to force rebuilding the module files when the thirdparty library is updated.
env.Depends(module_obj, thirdparty_obj)
20 changes: 20 additions & 0 deletions modules/fbx/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def can_build(env, platform):
env.module_add_dependencies("fbx", ["gltf"])
return not env["disable_3d"]


def configure(env):
pass


def get_doc_classes():
return [
"EditorSceneFormatImporterFBX2GLTF",
"EditorSceneFormatImporterUFBX",
"FBXDocument",
"FBXState",
]


def get_doc_path():
return "doc_classes"
13 changes: 13 additions & 0 deletions modules/fbx/doc_classes/EditorSceneFormatImporterFBX2GLTF.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSceneFormatImporterFBX2GLTF" inherits="EditorSceneFormatImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Importer for the [code].fbx[/code] scene file format.
</brief_description>
<description>
Imports Autodesk FBX 3D scenes by way of converting them to glTF 2.0 using the FBX2glTF command line tool.
The location of the FBX2glTF binary is set via the [member EditorSettings.filesystem/import/fbx2gltf/fbx2gltf_path] editor setting.
This importer is only used if [member ProjectSettings.filesystem/import/fbx2gltf/enabled] is set to [code]true[/code].
</description>
<tutorials>
</tutorials>
</class>
11 changes: 11 additions & 0 deletions modules/fbx/doc_classes/EditorSceneFormatImporterUFBX.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSceneFormatImporterUFBX" inherits="EditorSceneFormatImporter" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Import FBX files using the ufbx library.
</brief_description>
<description>
EditorSceneFormatImporterUFBX is designed to load FBX files and supports both binary and ASCII FBX files from version 3000 onward. This class supports various 3D object types like meshes, skins, blend shapes, materials, and rigging information. The class aims for feature parity with the official FBX SDK and supports FBX 7.4 specifications.
</description>
<tutorials>
</tutorials>
</class>
12 changes: 12 additions & 0 deletions modules/fbx/doc_classes/FBXDocument.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FBXDocument" inherits="GLTFDocument" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Handles FBX documents.
</brief_description>
<description>
The FBXDocument handles FBX documents. It provides methods to append data from buffers or files, generate scenes, and register/unregister document extensions.
When exporting FBX from Blender, use the "FBX Units Scale" option. The "FBX Units Scale" option sets the correct scale factor and avoids manual adjustments when re-importing into Blender, such as through glTF export.
</description>
<tutorials>
</tutorials>
</class>
15 changes: 15 additions & 0 deletions modules/fbx/doc_classes/FBXState.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FBXState" inherits="GLTFState" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
</brief_description>
<description>
The FBXState handles the state data imported from FBX files.
</description>
<tutorials>
</tutorials>
<members>
<member name="allow_geometry_helper_nodes" type="bool" setter="set_allow_geometry_helper_nodes" getter="get_allow_geometry_helper_nodes" default="false">
If [code]true[/code], the import process used auxiliary nodes called geometry helper nodes. These nodes help preserve the pivots and transformations of the original 3D model during import.
</member>
</members>
</class>
Loading

0 comments on commit 04d4394

Please sign in to comment.