Skip to content
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
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
182 changes: 91 additions & 91 deletions tools/resource/mesh_resource_fbx.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -857,7 +943,7 @@ public class FBXImporter
}
}

if (options.import_animation.value)
if (smr != null)
shader += "+SKINNING";

// Create .material resource.
Expand All @@ -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;
Expand Down
6 changes: 5 additions & 1 deletion tools/widgets/save_resource_dialog.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down