Skip to content
Open
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
52 changes: 41 additions & 11 deletions exporters/scene_exporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1875,7 +1875,7 @@
}
if (meshes.size() > 1) {
SurfaceTool surface_tool;
for (size_t i = 0; i < meshes.size(); i++) {

Check warning on line 1878 in exporters/scene_exporter.cpp

View workflow job for this annotation

GitHub Actions / Windows Template Release

'<': signed/unsigned mismatch

Check warning on line 1878 in exporters/scene_exporter.cpp

View workflow job for this annotation

GitHub Actions / Windows Editor

'<': signed/unsigned mismatch
for (size_t j = 0; j < meshes[i]->get_surface_count(); j++) {
if (i == 0 && j == 0) {
surface_tool.create_from(meshes[i], j);
Expand Down Expand Up @@ -2849,7 +2849,7 @@
return root;
}

Error GLBExporterInstance::_load_scene_and_deps(Ref<PackedScene> &r_scene) {
Error GLBExporterInstance::_load_scene_and_deps(Ref<Resource> &r_scene) {
MeshInstance3D::upgrading_skeleton_compat = true;
err = _load_deps();
if (err != OK) {
Expand All @@ -2858,23 +2858,23 @@
return _load_scene(r_scene);
}

Error GLBExporterInstance::_load_scene(Ref<PackedScene> &r_scene) {
Error GLBExporterInstance::_load_scene(Ref<Resource> &r_scene) {
auto mode_type = ResourceCompatLoader::get_default_load_type();
// loading older scenes will spam warnings about deprecated features
#ifndef DEBUG_ENABLED
if (ver_major <= 3) {
_silence_errors(true);
}
#endif
std::optional<Ref<PackedScene>> result;
std::optional<Ref<Resource>> result;
// For some reason, scenes with meshes fail to load without the load done by ResourceLoader::load, possibly due to notification shenanigans.
if (ResourceCompatLoader::is_globally_available()) {
result = TaskManager::get_singleton()->dispatch_to_main_thread((std::function<Ref<PackedScene>()>)[&]() -> Ref<PackedScene> {
return ResourceLoader::load(source_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
result = TaskManager::get_singleton()->dispatch_to_main_thread((std::function<Ref<Resource>()>)[&]() -> Ref<Resource> {
return ResourceLoader::load(source_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
});
} else {
result = TaskManager::get_singleton()->dispatch_to_main_thread((std::function<Ref<PackedScene>()>)[&]() -> Ref<PackedScene> {
return ResourceCompatLoader::custom_load(source_path, "PackedScene", mode_type, &err, using_threaded_load(), ResourceFormatLoader::CACHE_MODE_REUSE);
result = TaskManager::get_singleton()->dispatch_to_main_thread((std::function<Ref<Resource>()>)[&]() -> Ref<Resource> {
return ResourceCompatLoader::custom_load(source_path, "", mode_type, &err, using_threaded_load(), ResourceFormatLoader::CACHE_MODE_REUSE);
});
}
if (!result.has_value()) {
Expand Down Expand Up @@ -3294,6 +3294,18 @@
return _check_unsupported(ver_major, is_text_output());
}

Error create_packed_scene_from_mesh(const Ref<Mesh> &mesh, Ref<PackedScene> &scene) {
ERR_FAIL_COND_V_MSG(mesh.is_null(), ERR_INVALID_PARAMETER, "Mesh is null");
return TaskManager::get_singleton()->dispatch_to_main_thread((std::function<Error()>)[&]() -> Error {
MeshInstance3D *root = memnew(MeshInstance3D);
root->set_mesh(mesh);
scene = Ref<PackedScene>(memnew(PackedScene));
scene->pack(root);
return OK;
})
.value_or(ERR_SKIP);
}

// scene loading and scene instancing has to be done on the main thread to avoid deadlocks and crashes
bool batch_preload() {
GDRELogger::clear_error_queues();
Expand All @@ -3318,13 +3330,31 @@

err = gdre::ensure_dir(p_dest_path.get_base_dir());
report->set_error(err);
ERR_FAIL_COND_V_MSG(err, false, "Failed to ensure directory " + p_dest_path.get_base_dir());
if (err) {
after_preload();
ERR_FAIL_V_MSG(false, "Failed to ensure directory " + p_dest_path.get_base_dir());
}
{
Ref<PackedScene> scene;
err = instance._load_scene_and_deps(scene);
if (scene.is_null() && err == OK) {
String resource_type = report->get_import_info()->get_type();
bool is_mesh = false;
if (resource_type != "PackedScene") {
if (resource_type != "Mesh" && !ClassDB::is_parent_class(resource_type, "Mesh")) {
after_preload();
ERR_FAIL_V_MSG(false, "Unsupported resource type: " + resource_type);
}
is_mesh = true;
}
Ref<Resource> resource;
err = instance._load_scene_and_deps(resource);
if (resource.is_null() && err == OK) {
err = ERR_CANT_ACQUIRE_RESOURCE;
}

Ref<PackedScene> scene = resource;
if (err == OK && scene.is_null() && is_mesh) {
err = create_packed_scene_from_mesh(resource, scene);
}

if (err != OK) {
report->set_error(err);
after_preload();
Expand Down Expand Up @@ -3602,7 +3632,7 @@
Vector<String> GLBExporterInstance::_get_logged_error_messages() {
auto errors = supports_multithread() ? GDRELogger::get_thread_errors() : GDRELogger::get_errors();
Vector<String> ret;
for (auto &err : errors) {

Check warning on line 3635 in exporters/scene_exporter.cpp

View workflow job for this annotation

GitHub Actions / Windows Template Release

declaration of 'err' hides class member

Check warning on line 3635 in exporters/scene_exporter.cpp

View workflow job for this annotation

GitHub Actions / Windows Editor

declaration of 'err' hides class member
String lstripped = err.strip_edges(true, false);
if (!lstripped.begins_with("GDScript backtrace")) {
ret.push_back(err.strip_edges(false, true));
Expand Down
4 changes: 2 additions & 2 deletions exporters/scene_exporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ class GLBExporterInstance {
Dictionary _get_default_subresource_options();
Error _check_model_can_load(const String &p_dest_path);
Error _load_deps();
Error _load_scene_and_deps(Ref<PackedScene> &r_scene);
Error _load_scene(Ref<PackedScene> &r_scene);
Error _load_scene_and_deps(Ref<Resource> &r_scene);
Error _load_scene(Ref<Resource> &r_scene);
void recompute_animation_tracks_for_library(AnimationPlayer *p_player, const Ref<AnimationLibrary> &p_anim_lib, const LocalVector<StringName> &p_anim_names);
void convert_animation_tracks_to_v4_for_player(AnimationPlayer *p_player);

Expand Down
72 changes: 70 additions & 2 deletions standalone/gdre_recover.gd
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func _get_all_files(files: PackedStringArray) -> PackedStringArray:

const DIR_STRUCTURE_OPTION_NAME = "Directory Structure"
const EXPORT_SCENE_OPTION_NAME = "Export Scenes as"
const EXPORT_MESH_OPTION_NAME = "Export Meshes as"

enum DirStructure {
FLAT,
Expand All @@ -96,6 +97,14 @@ enum ExportSceneType {
GLTF
}

enum ExportMeshType {
AUTO,
TRES,
OBJ,
GLB,
GLTF
}

const DIR_STRUCTURE_NAMES: PackedStringArray = [
"Flat",
"Relative Hierarchical",
Expand All @@ -109,6 +118,14 @@ const EXPORT_SCENE_TYPE_NAMES: PackedStringArray = [
"GLTF",
]

const EXPORT_MESH_TYPE_NAMES: PackedStringArray = [
"Auto",
"tres",
"OBJ",
"GLB",
"GLTF",
]

func get_output_file_name(src: String, output_folder: String, dir_structure_option: DirStructure, new_ext: String = "", rel_base: String = "") -> String:
var new_name = ""
if dir_structure_option == DirStructure.FLAT:
Expand Down Expand Up @@ -150,6 +167,44 @@ func _export_scene(file: String, output_dir: String, dir_structure: DirStructure
report.error = OK
return report

# TODO: A more generic way to export resources, stop copying all this code around
func _export_mesh(file: String, output_dir: String, dir_structure: DirStructure, rel_base: String, export_type: ExportMeshType) -> ExportReport:
var source_file = file
var iinfo = GDRESettings.get_import_info_by_dest(file)
if iinfo:
source_file = iinfo.source_file

var ext = source_file.get_extension().to_lower()

if export_type == ExportMeshType.GLB:
ext = "glb"
elif export_type == ExportMeshType.GLTF:
ext = "gltf"
elif export_type == ExportMeshType.OBJ:
ext = "obj"
elif export_type == ExportMeshType.TRES:
ext = "tres"
else: # AUTO
if not is_instance_valid(iinfo):
ext = "tres"

var report: ExportReport = ExportReport.new()
var export_dest = get_output_file_name(source_file, output_dir, dir_structure, ext, rel_base)
if ext == "tres":
# just use bin to text
report.error = ResourceCompatLoader.to_text(file, export_dest)
return report

if export_type == ExportMeshType.OBJ:
report.error = ObjExporter.export_file_with_options(export_dest, file, {})
else:
report = SceneExporter.export_file_with_options(export_dest, file, {
"Exporter/Scene/GLTF/replace_shader_materials": true,
})
if (report.error == ERR_BUG or report.error == ERR_PRINTER_ON_FIRE or report.error == ERR_DATABASE_CANT_READ):
report.error = OK
return report


func get_log_error_string(errs: PackedStringArray) -> String:
return "\n".join(GDRECommon.filter_error_backtraces(errs))
Expand All @@ -167,7 +222,7 @@ func convert_pcfg_to_text(path: String, output_dir: String) -> Array:
return [err, text_file]
return [loader.save_cfb(output_dir, ver_major, ver_minor), text_file]

func _export_files(files: PackedStringArray, output_dir: String, dir_structure: DirStructure, rel_base: String, export_glb: ExportSceneType) -> PackedStringArray:
func _export_files(files: PackedStringArray, output_dir: String, dir_structure: DirStructure, rel_base: String, export_glb: ExportSceneType, export_mesh: ExportMeshType) -> PackedStringArray:
var errs: PackedStringArray = []
files = _get_all_files(files)

Expand Down Expand Up @@ -201,6 +256,12 @@ func _export_files(files: PackedStringArray, output_dir: String, dir_structure:
errs.append("Exporting cancelled: " + file + "\n" + report.message + "\n" + get_log_error_string(report.get_error_messages()))
break
errs.append("Failed to export resource: " + file + "\n" + report.message + "\n" + get_log_error_string(report.get_error_messages()))
if file_ext == "mesh" or (_ret and _ret.get_compat_type().contains("Mesh")) and export_mesh != ExportMeshType.AUTO:
var report: ExportReport = _export_mesh(file, output_dir, dir_structure, rel_base, export_mesh)
if not report:
errs.append("Failed to export resource: " + file + get_log_error_string(GDRESettings.get_errors()))
elif report.error != OK and report.error != ERR_PRINTER_ON_FIRE:
errs.append("Failed to export resource: " + file + "\n" + report.message + "\n" + get_log_error_string(report.get_error_messages()))
elif _ret:
var iinfo: ImportInfo = ImportInfo.copy(_ret)
iinfo.export_dest = get_output_file_name(iinfo.source_file, "res://", dir_structure, iinfo.source_file.get_extension().to_lower(), rel_base)
Expand Down Expand Up @@ -263,8 +324,9 @@ func _do_export(output_dir: String, export_preview_visible: bool):
var options = %ExportResDirDialog.get_selected_options()
var dir_structure = options.get(DIR_STRUCTURE_OPTION_NAME, DirStructure.RELATIVE_HIERARCHICAL)
var export_glb: ExportSceneType = options.get(EXPORT_SCENE_OPTION_NAME, int(ExportSceneType.AUTO))
var export_mesh: ExportMeshType = options.get(EXPORT_MESH_OPTION_NAME, int(ExportMeshType.AUTO))

errs = _export_files(files, output_dir, dir_structure, rel_base, export_glb)
errs = _export_files(files, output_dir, dir_structure, rel_base, export_glb, export_mesh)
if export_preview_visible:
%GdreResourcePreview.set_main_view_visible(true)

Expand Down Expand Up @@ -371,6 +433,12 @@ func _set_file_dialog_options(file_dialog: FileDialog, default_dir_structure: Di
if not include_scene:
return
var include_glb = GDRESettings.get_ver_major() >= SceneExporter.get_minimum_godot_ver_supported()
var mesh_default = options.get(EXPORT_MESH_OPTION_NAME, int(ExportMeshType.AUTO))
var mesh_opts = EXPORT_MESH_TYPE_NAMES.duplicate()
if not include_glb:
mesh_opts.remove_at(int(ExportMeshType.GLTF))
mesh_opts.remove_at(int(ExportMeshType.GLB))
file_dialog.add_option(EXPORT_MESH_OPTION_NAME, mesh_opts, mesh_default)
var scene_default = options.get(EXPORT_SCENE_OPTION_NAME, int(ExportSceneType.AUTO))
#file_dialog.set_option_default(0, int(default_dir_structure))
var glb_opts = EXPORT_SCENE_TYPE_NAMES.duplicate()
Expand Down
Loading