diff --git a/docs/changelog.rst b/docs/changelog.rst index 4b0918ec16..6885c7dfd0 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -33,6 +33,8 @@ Changelog * Tools: fixed re-importing of units from 3D scene files. * Tools: fixed transforms in unit resource losing the 'name' property if saved in the Unit Editor. * Tools: Linux: fixed a number of UI theming issues. +* Tools: fixed a minor serialization issue affecting the .importer_settings file when importing FBX scenes. +* Tools: fixed an issue that caused non-skinned FBX scenes to import materials with SKINNED option enabled in some cases. * Runtime: fixed a race conditions during application exit. * Runtime: fixed a race condition when loading resources. * Runtime: fixed a potential buffer overflow when raycasting animated sprites. diff --git a/tools/resource/mesh_resource_fbx.vala b/tools/resource/mesh_resource_fbx.vala index da97dc5f34..3a734fc298 100644 --- a/tools/resource/mesh_resource_fbx.vala +++ b/tools/resource/mesh_resource_fbx.vala @@ -169,7 +169,7 @@ public class FBXImportOptions else if (g.key == "new_skeleton") new_skeleton.value = (bool)g.value; else if (g.key == "target_skeleton") - target_skeleton.value = (string)g.value; + target_skeleton.value = (string)g.value != "" ? (string)g.value : null; else if (g.key == "import_clips") import_clips.value = (bool)g.value; else if (g.key == "create_animations_folder") @@ -207,7 +207,7 @@ public class FBXImportOptions obj.set("import_materials", skip_units ? false : import_materials.value); obj.set("create_materials_folder", skip_units ? false : create_materials_folder.value); obj.set("new_skeleton", skip_anims ? false : new_skeleton.value); - obj.set("target_skeleton", skip_anims ? "" : target_skeleton.value); + obj.set("target_skeleton", (skip_anims || target_skeleton.value == null) ? "" : target_skeleton.value); obj.set("import_clips", skip_anims ? false : import_clips.value); obj.set("create_animations_folder", skip_anims ? false : create_animations_folder.value); @@ -316,11 +316,11 @@ public class FBXImportDialog : Gtk.Window if (res == ImportResult.SUCCESS && _filenames.size > 0) { GLib.File file_dst; string resource_path; - + get_destination_file(out file_dst, _destination_dir, File.new_for_path(_filenames[0])); get_resource_path(out resource_path, file_dst, _project); string resource_name = ResourceId.name(resource_path); - + primary_path = ResourceId.path(OBJECT_TYPE_UNIT, resource_name); } @@ -726,6 +726,92 @@ public class FBXImporter } } + // Import animations. + StateMachineResource? smr = null; + + if (options.import_animation.value) { + string target_skeleton = options.target_skeleton.value; + + // Import animation skeleton. + if (options.new_skeleton.value) { + // Create .animation_skeleton resource. + unowned ufbx.Node? skeleton_root_node = find_skeleton_root(scene.root_node); + if (skeleton_root_node != null) { + Guid skeleton_hierarchy_id = Guid.new_guid(); + import_skeleton(options + , db + , GUID_ZERO + , skeleton_hierarchy_id + , skeleton_root_node + ); + + Guid animation_skeleton_id = Guid.new_guid(); + db.create(animation_skeleton_id, OBJECT_TYPE_MESH_SKELETON); + db.set_string(animation_skeleton_id, "source", resource_path); + db.add_to_set(animation_skeleton_id, "skeleton", skeleton_hierarchy_id); + if (db.save(project.absolute_path(resource_name) + "." + OBJECT_TYPE_MESH_SKELETON, animation_skeleton_id) != 0) + return ImportResult.ERROR; + + target_skeleton = resource_name; + + // Create .state_machine resource to drive the skeleton. + Guid state_machine_id = Guid.new_guid(); + smr = StateMachineResource.mesh(db + , state_machine_id + , target_skeleton + ); + if (smr.save(project, resource_name) != 0) + return ImportResult.ERROR; + } + } + + // Import animation clip. + if (options.import_clips.value) { + if (target_skeleton == "") { + logw("Animation must have a target skeleton. Animation clips won't be imported."); + } else { + // Create 'animations' folder. + string directory_name = "animations"; + string animations_path = destination_dir; + if (options.create_animations_folder.value && scene.anim_stacks.data.length != 0) { + GLib.File animations_file = File.new_for_path(Path.build_filename(destination_dir, directory_name)); + try { + animations_file.make_directory(); + } catch (GLib.IOError.EXISTS e) { + // Ignore. + } catch (GLib.Error e) { + loge(e.message); + return ImportResult.ERROR; + } + + animations_path = animations_file.get_path(); + } + + // Extract clips. + if (scene.anim_stacks.data.length > 0) { + unowned ufbx.AnimStack anim_stack = scene.anim_stacks.data[0]; + + string anim_filename = Path.build_filename(animations_path, resource_basename + "." + OBJECT_TYPE_MESH_ANIMATION); + GLib.File anim_file = GLib.File.new_for_path(anim_filename); + string anim_path = anim_file.get_path(); + + string anim_resource_filename = project.resource_filename(anim_path); + string anim_resource_path = ResourceId.normalize(anim_resource_filename); + string anim_resource_name = ResourceId.name(anim_resource_path); + + // Create .mesh_animation resource. + Guid anim_id = Guid.new_guid(); + db.create(anim_id, OBJECT_TYPE_MESH_ANIMATION); + db.set_string(anim_id, "source", resource_path); + db.set_string(anim_id, "target_skeleton", target_skeleton); + db.set_string(anim_id, "stack_name", (string)anim_stack.name.data); + if (db.save(project.absolute_path(anim_resource_name) + "." + OBJECT_TYPE_MESH_ANIMATION, anim_id) != 0) + return ImportResult.ERROR; + } + } + } + } + // Import materials. if (options.import_units.value && options.import_materials.value) { // Create 'materials' folder. @@ -857,7 +943,7 @@ public class FBXImporter } } - if (options.import_animation.value) + if (smr != null) shader += "+SKINNING"; // Create .material resource. @@ -883,92 +969,6 @@ public class FBXImporter } } - // Import animations. - StateMachineResource? smr = null; - - if (options.import_animation.value) { - string target_skeleton = options.target_skeleton.value; - - // Import animation skeleton. - if (options.new_skeleton.value) { - // Create .animation_skeleton resource. - unowned ufbx.Node? skeleton_root_node = find_skeleton_root(scene.root_node); - if (skeleton_root_node != null) { - Guid skeleton_hierarchy_id = Guid.new_guid(); - import_skeleton(options - , db - , GUID_ZERO - , skeleton_hierarchy_id - , skeleton_root_node - ); - - Guid animation_skeleton_id = Guid.new_guid(); - db.create(animation_skeleton_id, OBJECT_TYPE_MESH_SKELETON); - db.set_string(animation_skeleton_id, "source", resource_path); - db.add_to_set(animation_skeleton_id, "skeleton", skeleton_hierarchy_id); - if (db.save(project.absolute_path(resource_name) + "." + OBJECT_TYPE_MESH_SKELETON, animation_skeleton_id) != 0) - return ImportResult.ERROR; - - target_skeleton = resource_name; - - // Create .state_machine resource to drive the skeleton. - Guid state_machine_id = Guid.new_guid(); - smr = StateMachineResource.mesh(db - , state_machine_id - , target_skeleton - ); - if (smr.save(project, resource_name) != 0) - return ImportResult.ERROR; - } - } - - // Import animation clip. - if (options.import_clips.value) { - if (target_skeleton == "") { - logw("Animation must have a target skeleton. Animation clips won't be imported."); - } else { - // Create 'animations' folder. - string directory_name = "animations"; - string animations_path = destination_dir; - if (options.create_animations_folder.value && scene.anim_stacks.data.length != 0) { - GLib.File animations_file = File.new_for_path(Path.build_filename(destination_dir, directory_name)); - try { - animations_file.make_directory(); - } catch (GLib.IOError.EXISTS e) { - // Ignore. - } catch (GLib.Error e) { - loge(e.message); - return ImportResult.ERROR; - } - - animations_path = animations_file.get_path(); - } - - // Extract clips. - if (scene.anim_stacks.data.length > 0) { - unowned ufbx.AnimStack anim_stack = scene.anim_stacks.data[0]; - - string anim_filename = Path.build_filename(animations_path, resource_basename + "." + OBJECT_TYPE_MESH_ANIMATION); - GLib.File anim_file = GLib.File.new_for_path(anim_filename); - string anim_path = anim_file.get_path(); - - string anim_resource_filename = project.resource_filename(anim_path); - string anim_resource_path = ResourceId.normalize(anim_resource_filename); - string anim_resource_name = ResourceId.name(anim_resource_path); - - // Create .mesh_animation resource. - Guid anim_id = Guid.new_guid(); - db.create(anim_id, OBJECT_TYPE_MESH_ANIMATION); - db.set_string(anim_id, "source", resource_path); - db.set_string(anim_id, "target_skeleton", target_skeleton); - db.set_string(anim_id, "stack_name", (string)anim_stack.name.data); - if (db.save(project.absolute_path(anim_resource_name) + "." + OBJECT_TYPE_MESH_ANIMATION, anim_id) != 0) - return ImportResult.ERROR; - } - } - } - } - if (options.import_units.value) { // Generate or modify existing .unit. Guid unit_id; diff --git a/tools/widgets/save_resource_dialog.vala b/tools/widgets/save_resource_dialog.vala index 8a27faa93b..7f16519151 100644 --- a/tools/widgets/save_resource_dialog.vala +++ b/tools/widgets/save_resource_dialog.vala @@ -43,7 +43,11 @@ public class SaveResourceDialog : Gtk.FileChooserDialog public void on_response(int response_id) { - string? path = this.get_file().get_path(); + GLib.File? file = this.get_file(); + if (file == null) + return; + + string? path = file.get_path(); if (response_id == Gtk.ResponseType.ACCEPT && path != null) { if (!path.has_suffix("." + _resource_type))