Skip to content

Commit

Permalink
git subrepo pull --force addons/unidot_importer
Browse files Browse the repository at this point in the history
subrepo:
  subdir:   "addons/unidot_importer"
  merged:   "8fdd042"
upstream:
  origin:   "https://github.com/V-Sekai/unidot_importer.git"
  branch:   "main"
  commit:   "8fdd042"
git-subrepo:
  version:  "0.4.6"
  origin:   "https://github.com/ingydotnet/git-subrepo"
  commit:   "110b9eb"
  • Loading branch information
fire committed May 10, 2024
1 parent 8bfbc8e commit ba300db
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 121 deletions.
4 changes: 2 additions & 2 deletions addons/unidot_importer/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/V-Sekai/unidot_importer.git
branch = main
commit = f5008905071c8ce4c84f409dc1231df72bcb3407
parent = c4641e9e4cdbba8f30a3eddc3bb59eecc70315cd
commit = 8fdd0423f960dc11391bd7b829aa196a45d5c7c8
parent = 8bfbc8eebf5e9ec6bc002d2ac0e5151577cd2472
method = merge
cmdver = 0.4.6
449 changes: 364 additions & 85 deletions addons/unidot_importer/asset_adapter.gd

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions addons/unidot_importer/asset_database.gd
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var log_message_holder = asset_meta_class.LogMessageHolder.new()
@export var enable_verbose_logs: bool = false
@export var set_animation_trees_active: bool = true
@export var vrm_spring_bones: bool = true
@export var convert_fbx_to_gltf: bool = false
var vrm_integration_plugin
var log_limit_per_guid: int = 100000

Expand Down
13 changes: 11 additions & 2 deletions addons/unidot_importer/asset_meta.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const yaml_parser_class: GDScript = preload("./yaml_parser.gd")
const object_adapter_class: GDScript = preload("./object_adapter.gd")
const bin_parser_class: GDScript = preload("./deresuteme/decode.gd")
const unidot_utils_class: GDScript = preload("./unidot_utils.gd")
var unidot_utils = unidot_utils_class.new()
var unidot_utils := unidot_utils_class.new()


class DatabaseHolder:
Expand Down Expand Up @@ -67,6 +67,7 @@ var prefab_fileid_to_skeleton_bone = {} # int -> string
var prefab_fileid_to_utype = {} # int -> int
var prefab_type_to_fileids = {} # int -> int
var prefab_fileid_to_gameobject_fileid: Dictionary = {} # int -> int
var prefab_fileid_to_material_order_rev: Dictionary = {}
var fileid_to_component_fileids: Dictionary = {} # int -> int
# TODO: remove @export
@export var prefab_gameobject_name_to_fileid_and_children: Dictionary = {} # {null: 400000, "SomeName": {null: 1234, "SomeName2": ...}
Expand All @@ -83,6 +84,7 @@ var fileid_to_component_fileids: Dictionary = {} # int -> int
@export var fileid_to_skeleton_bone: Dictionary = {} # int -> string: scene_node_state.add_fileID_to_skeleton_bone
@export var fileid_to_utype: Dictionary = {} # int -> int: parse_binary_asset/parse_asset
@export var fileid_to_gameobject_fileid: Dictionary = {} # int -> int: parse_binary_asset/parse_asset
@export var fileid_to_material_order_rev: Dictionary = {} # Mesh OR MeshRenderer fileID int -> PackedInt32Array
@export var type_to_fileids: Dictionary = {} # string -> Array[int]: parse_binary_asset/parse_asset
@export var godot_resources: Dictionary = {} # int -> Resource: insert_resource/override_resource
@export var main_object_id: int = 0 # e.g. 2100000 for .mat; 100000 for .fbx or GameObject; 100100000 for .prefab
Expand All @@ -104,6 +106,9 @@ var fileid_to_component_fileids: Dictionary = {} # int -> int
@export var imported_mesh_paths: Dictionary # mesh_name (with and without "Root Scene_") -> file_path
@export var imported_material_paths: Dictionary # material_name -> file_path

var taken_over_import_references: Dictionary = {} # String -> Resource
@export var unique_texture_map: Dictionary

class ParsedAsset:
extends RefCounted
var local_id_alias: Dictionary = {} # type*100000 + file_index*2 -> real fileId
Expand Down Expand Up @@ -253,7 +258,7 @@ func is_humanoid() -> bool:
func is_using_builtin_ufbx() -> bool:
# if Engine.get_version_info().hex >= 0x040300:
if ClassDB.class_exists(&"FBXDocument") and ClassDB.class_exists(&"FBXState"):
return true
return not log_database_holder.database.convert_fbx_to_gltf
return false


Expand Down Expand Up @@ -423,6 +428,10 @@ func remap_prefab_fileids(prefab_fileid: int, target_prefab_meta: Resource):
self.prefab_transform_fileid_to_scale_signs[xor_or_stripped(target_fileid, prefab_fileid)] = target_prefab_meta.transform_fileid_to_scale_signs.get(target_fileid)
for target_fileid in target_prefab_meta.prefab_transform_fileid_to_scale_signs:
self.prefab_transform_fileid_to_scale_signs[xor_or_stripped(target_fileid, prefab_fileid)] = target_prefab_meta.prefab_transform_fileid_to_scale_signs.get(target_fileid)
for target_fileid in target_prefab_meta.fileid_to_material_order_rev:
self.prefab_fileid_to_material_order_rev[xor_or_stripped(target_fileid, prefab_fileid)] = target_prefab_meta.fileid_to_material_order_rev.get(target_fileid)
for target_fileid in target_prefab_meta.prefab_fileid_to_material_order_rev:
self.prefab_fileid_to_material_order_rev[xor_or_stripped(target_fileid, prefab_fileid)] = target_prefab_meta.prefab_fileid_to_material_order_rev.get(target_fileid)


func calculate_prefab_nodepaths_recursive():
Expand Down
4 changes: 3 additions & 1 deletion addons/unidot_importer/convert_scene.gd
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ func pack_scene(pkgasset, is_prefab) -> PackedScene:
env.fog_light_energy = max_c
if asset.keys.get("m_FogMode", 3) == 1: # Linear
const TARGET_FOG_DENSITY = 0.05
env.fog_density = -log(TARGET_FOG_DENSITY) / asset.keys.get("m_LinearFogEnd", 0.0)
# It's not possible to emulate linear fog exactly with the level of control we currently have.
# Multiply by 0.1 because the fog seemed too aggressive.
env.fog_density = 0.1 * -log(TARGET_FOG_DENSITY) / asset.keys.get("m_LinearFogEnd", 0.0)
else:
env.fog_density = asset.keys.get("m_FogDensity", 0.0)
var sun: Array = asset.keys.get("m_Sun", [null, 0, null, null])
Expand Down
43 changes: 36 additions & 7 deletions addons/unidot_importer/object_adapter.gd
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class UnidotObject:
last_material = node.get_active_material(old_surface_count - 1)

var last_extra_material: Resource = last_material
var current_materials: Array = [].duplicate()
var current_materials_raw: Array = [].duplicate()

var prefix: String = material_prefix + str(old_surface_count - 1) + ":"
var new_prefix: String = prefix
Expand All @@ -315,9 +315,18 @@ class UnidotObject:
var mat: Resource = node.get_active_material(material_idx)
if mat != null and str(mat.resource_name).begins_with(prefix):
break
current_materials.push_back(mat)
current_materials_raw.push_back(mat)
material_idx += 1

var mat_slots: PackedInt32Array = meta.fileid_to_material_order_rev.get(fileID, meta.prefab_fileid_to_material_order_rev.get(fileID, PackedInt32Array()))
var current_materials: Array
current_materials.resize(len(mat_slots))
for i in range(len(mat_slots)):
current_materials[i] = current_materials_raw[mat_slots[i]]
for i in range(len(mat_slots), len(current_materials_raw)):
current_materials.append(current_materials_raw[i])
log_debug("Current mat slots " + str(mat_slots) + " materials before: " + str(current_materials))

while last_extra_material != null and (str(last_extra_material.resource_name).begins_with(prefix) or str(last_extra_material.resource_name).begins_with(new_prefix)):
if str(last_extra_material.resource_name).begins_with(new_prefix):
prefix = new_prefix
Expand All @@ -335,6 +344,11 @@ class UnidotObject:
var new_materials_size = props.get("_materials_size", material_idx)

if props.has("_mesh"):
var mesh_ref: Array = props["_mesh_ref"]
var mesh_meta: Resource = meta.lookup_meta(mesh_ref)
if mesh_meta != null:
meta.fileid_to_material_order_rev[fileID] = mesh_meta.fileid_to_material_order_rev.get(mesh_ref[1], PackedInt32Array())
log_debug("GET " + str(fileID) + ": " + str(mesh_ref[1]) + " from " + str(mesh_meta.fileid_to_material_order_rev.get(mesh_ref[1])))
node.mesh = props.get("_mesh")
node.material_override = null

Expand All @@ -356,9 +370,15 @@ class UnidotObject:
# GI_MODE_DISABLED seems buggy and ignores light probes.
node.gi_mode = GeometryInstance3D.GI_MODE_DYNAMIC


mat_slots = meta.fileid_to_material_order_rev.get(fileID, meta.prefab_fileid_to_material_order_rev.get(fileID, PackedInt32Array()))
current_materials.resize(new_materials_size)
for i in range(new_materials_size):
current_materials[i] = props.get("_materials/" + str(i), current_materials[i])
var idx = mat_slots[i] if i < len(mat_slots) else i
if idx < new_materials_size:
current_materials[idx] = props.get("_materials/" + str(i), current_materials[i])

log_debug("After mat slots " + str(mat_slots) + " materials after: " + str(current_materials))

var new_surface_count: int = 0 if node.mesh == null else node.mesh.get_surface_count()
if new_surface_count != 0 and node.mesh != null:
Expand Down Expand Up @@ -4342,7 +4362,9 @@ class UnidotPrefabInstance:
instanced_scene.scene_file_path = ""
set_owner_rec(instanced_scene, state.owner)
var anim_player: AnimationPlayer = instanced_scene.get_node_or_null("AnimationPlayer") as AnimationPlayer
if anim_player != null and anim_player.has_animation(&"RESET"):
# Scenes with only a RESET but not a unidot-created _T-Pose_ are following the Godot convention of rest-pose as RESET.
# In Godot engine versions which support rest-as-RESET, the model will already be posed correctly and we do not want this.
if anim_player != null and anim_player.has_animation(&"RESET") and anim_player.has_animation(&"_T-Pose_"):
var root_node: Node = anim_player.get_node(anim_player.root_node)
var reset_anim: Animation = anim_player.get_animation(&"RESET")
if reset_anim != null:
Expand Down Expand Up @@ -5431,6 +5453,7 @@ class UnidotMeshFilter:
var new_mesh: Mesh = meta.get_godot_resource(mesh_ref)
log_debug("MeshFilter " + str(self) + " ref " + str(mesh_ref) + " new mesh " + str(new_mesh) + " old mesh " + str(node.mesh))
outdict["_mesh"] = new_mesh # property track?
outdict["_mesh_ref"] = mesh_ref # property track?
return outdict

func get_filter_mesh() -> Array: # UnidotRef
Expand Down Expand Up @@ -5513,16 +5536,21 @@ class UnidotMeshRenderer:
assign_object_meta(new_node)
if meta.get_database().enable_unidot_keys:
new_node.editor_description = str(self)
new_node.mesh = meta.get_godot_resource(self.get_mesh())
var mesh_ref: Array = self.get_mesh()
var mesh_meta: Resource = meta.lookup_meta(mesh_ref)
if mesh_meta != null:
new_node.mesh = meta.get_godot_resource(mesh_ref)
meta.fileid_to_material_order_rev[fileID] = mesh_meta.fileid_to_material_order_rev.get(mesh_ref[1], PackedInt32Array())

if is_stripped or gameObject.is_stripped:
log_fail("Oh no i am stripped MeshRenderer create_godot_node_orig")
var mf: RefCounted = gameObject.get_meshFilter()
if mf != null:
state.add_fileID(new_node, mf)
var idx: int = 0
var mat_slots: PackedInt32Array = meta.fileid_to_material_order_rev.get(fileID, meta.prefab_fileid_to_material_order_rev.get(fileID, PackedInt32Array()))
for m in keys.get("m_Materials", []):
new_node.set_surface_override_material(idx, meta.get_godot_resource(m))
new_node.set_surface_override_material(mat_slots[idx] if idx < len(mat_slots) else idx, meta.get_godot_resource(m))
idx += 1
return new_node

Expand Down Expand Up @@ -5648,14 +5676,15 @@ class UnidotSkinnedMeshRenderer:
var skin_rotation_delta: Transform3D = skin_humanoid_rotation_delta.get(skin.get_bind_name(idx), Transform3D.IDENTITY)
var rotation_delta: Transform3D = meta.transform_fileid_to_rotation_delta.get(bone_fileID, meta.prefab_transform_fileid_to_rotation_delta.get(bone_fileID))
if !rotation_delta.is_equal_approx(skin_rotation_delta):
log_debug("skin " + str(idx) + " : This fileID is a humanoid bone rotation offset=" + str(rotation_delta.basis.get_rotation_quaternion()) + " scale " + str(rotation_delta.basis.get_scale()))
log_debug("skin " + str(idx) + " : This fileID is a humanoid bone rotation offset=" + str(rotation_delta.basis.get_rotation_quaternion()) + " scale " + str(rotation_delta.basis.get_scale()) + " pos " + str(rotation_delta.origin))
skin.set_bind_pose(idx, rotation_delta * skin_rotation_delta.affine_inverse() * skin.get_bind_pose(idx))
return skin

func convert_properties(node: Node, uprops: Dictionary) -> Dictionary:
var outdict = super.convert_properties(node, uprops)
if uprops.has("m_Mesh"):
var mesh_ref: Array = get_ref(uprops, "m_Mesh")
outdict["_mesh_ref"] = mesh_ref
var new_mesh: Mesh = meta.get_godot_resource(mesh_ref)
outdict["_mesh"] = new_mesh # property track?
var skin_ref: Array = mesh_ref
Expand Down
63 changes: 62 additions & 1 deletion addons/unidot_importer/package_import_dialog.gd
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ var debug_disable_silhouette_fix_checkbox: CheckBox
var force_humanoid_checkbox: CheckBox
var enable_verbose_log_checkbox: CheckBox
var enable_vrm_spring_bones_checkbox: CheckBox
var convert_fbx_to_gltf_checkbox: CheckBox

var batch_import_list_widget: ItemList
var batch_import_add_button: Button
Expand Down Expand Up @@ -122,6 +123,8 @@ var asset_yaml_post_model: Array = [].duplicate()
var asset_prefabs: Array = [].duplicate()
var asset_scenes: Array = [].duplicate()

var builtin_ufbx_supported: bool = ClassDB.class_exists(&"FBXDocument") and ClassDB.class_exists(&"FBXState")

var result_log_lineedit: TextEdit

var new_editor_plugin := EditorPlugin.new()
Expand Down Expand Up @@ -789,6 +792,12 @@ func _selected_package(p_path: String) -> void:
enable_vrm_spring_bones_checkbox.toggled.connect(self._enable_vrm_spring_bones_changed)
force_humanoid_checkbox = _add_checkbox_option("Import ALL scenes as humanoid retargeted skeletons", true if asset_database.force_humanoid else false)
force_humanoid_checkbox.toggled.connect(self._force_humanoid_changed)
if builtin_ufbx_supported:
convert_fbx_to_gltf_checkbox = _add_advanced_checkbox_option("Convert FBX models to glTF", true if asset_database.convert_fbx_to_gltf else false)
convert_fbx_to_gltf_checkbox.toggled.connect(self._convert_fbx_to_gltf_changed)
else:
convert_fbx_to_gltf_checkbox = _add_checkbox_option("Convert FBX models to glTF\n(Update to Godot 4.3 to disable)", true)
convert_fbx_to_gltf_checkbox.disabled = true

var vspace := Control.new()
vspace.custom_minimum_size = Vector2(0, 16)
Expand Down Expand Up @@ -948,6 +957,8 @@ func show_importer(ep: EditorPlugin) -> void:


func check_fbx2gltf():
if builtin_ufbx_supported:
return # No need to check for FBX2glTF on engines with native fbx.
var d = DirAccess.open("res://")
var addon_path: String = new_editor_plugin.get_editor_interface().get_editor_settings().get_setting("filesystem/import/fbx/fbx2gltf_path")
if not addon_path.get_file().is_empty():
Expand Down Expand Up @@ -1022,6 +1033,9 @@ func _enable_verbose_log_changed(val: bool):
func _enable_vrm_spring_bones_changed(val: bool):
asset_database.vrm_spring_bones = val

func _convert_fbx_to_gltf_changed(val: bool):
asset_database.convert_fbx_to_gltf = val

func _show_advanced_options_toggled(val: bool):
advanced_options_hbox.visible = val

Expand Down Expand Up @@ -1333,6 +1347,9 @@ func on_file_completed_godot_import(tw: RefCounted, loaded: bool):
asset_work_completed.append(tw)


var tmp_material_texture_files: PackedStringArray


func do_import_step():
if _currently_preprocessing_assets != 0:
asset_database.log_fail([null, 0, "", 0], "Import step called during preprocess")
Expand Down Expand Up @@ -1386,6 +1403,19 @@ func do_import_step():
asset_work_waiting_write.reverse()
asset_models = [].duplicate()
elif tree_dialog_state == STATE_IMPORTING_MODELS:
# Clear stale dummy texture file placeholders.
var dres = DirAccess.open("res://")
for file_path in tmp_material_texture_files:
var fa := FileAccess.open(file_path, FileAccess.READ)
if fa.get_length() != 0:
asset_database.log_debug([null, 0, "", 0], "Dummy texture " + str(file_path) + " is not very dummy")
continue
fa.close()
asset_database.log_debug([null, 0, "", 0], "Removing dummy empty texture " + str(file_path))
dres.remove(file_path)
dres.remove(file_path + ".import")
tmp_material_texture_files.clear()

update_progress_bar(10)
tree_dialog_state = STATE_IMPORTING_YAML_POST_MODEL
for tw in asset_yaml_post_model:
Expand Down Expand Up @@ -1465,6 +1495,7 @@ func do_import_step():
for tw in asset_work:
asset_work_currently_importing.push_back(tw)
if asset_adapter.uses_godot_importer(tw.asset):
asset_adapter.about_to_import(tw.asset)
tw.asset.log_debug("asset " + str(tw.asset) + " uses godot import")
asset_database.log_debug([null, 0, "", 0], "Importing " + str(tw.asset.pathname) + " with the godot importer...")
files_to_reimport.append("res://" + tw.asset.pathname)
Expand Down Expand Up @@ -1532,6 +1563,34 @@ func _done_preprocessing_assets():
import_worker2.start_threads(THREAD_COUNT)
#asset_adapter.write_sentinel_png(generate_sentinel_png_filename())

# Write temporary dummy texture file placeholders relative to .fbx files so the FBX importer will find them.
var dres = DirAccess.open("res://")
asset_database.log_debug([null, 0, "", 0], "Go through assets " + str(pkg.guid_to_pkgasset) + ".")
for guid in pkg.guid_to_pkgasset:
var pkgasset = pkg.guid_to_pkgasset.get(guid, null)
asset_database.log_debug([null, 0, "", 0], str(guid) + " " + str(pkgasset) + " " + str(pkgasset.pathname))
var dbgref = [null, 0, "", 0] # [null, pkgasset.parsed_meta.main_object_id, guid, 0]
if not pkgasset:
continue
asset_database.log_debug(dbgref, str(pkgasset.parsed_meta.unique_texture_map))
for tex_name in pkgasset.parsed_meta.unique_texture_map:
var tex_pathname: String = pkgasset.pathname.get_base_dir().path_join(tex_name)
if dres.file_exists(tex_pathname + ".import") or dres.file_exists(tex_pathname):
asset_database.log_debug(dbgref, "letsgooo already exists " + str(tex_pathname))
tmp_material_texture_files.append(tex_pathname)
continue
var fa := FileAccess.open(tex_pathname + ".import", FileAccess.WRITE_READ)
if fa == null:
continue
asset_database.log_debug(dbgref, "Writing " + str(tex_pathname))
fa.store_buffer("[remap]\n\nimporter=\"skip\"\n".to_utf8_buffer())
fa.close()
fa = FileAccess.open(tex_pathname, FileAccess.WRITE_READ)
asset_database.log_debug(dbgref, "Writing " + str(tex_pathname))
fa.close()
asset_database.log_debug(dbgref, "letsgooo")
tmp_material_texture_files.append(tex_pathname)

status_bar.text = "Converting textures to metal roughness..."
import_worker2.set_stage2(pkg.guid_to_pkgasset)
var visited = {}.duplicate()
Expand Down Expand Up @@ -1573,6 +1632,8 @@ func start_godot_import(tw: Object):
ti.erase_button(0, 0)
ti.set_custom_color(0, Color("#22bb66"))
ti.set_icon(0, status_success_icon)
if tw.asset.parsed_meta != null:
tw.asset.parsed_meta.taken_over_import_references.clear()
return

if asset_adapter.uses_godot_importer(tw.asset):
Expand Down Expand Up @@ -1893,7 +1954,7 @@ func _asset_tree_window_confirmed():
asset_database.log_limit_per_guid = 20000
else:
asset_database.log_limit_per_guid = 100000
status_bar.text = "Preprocessing and converting FBX2glTF..."
status_bar.text = "Preprocessing FBX and reading assets ..."
_preprocessing_second_pass = second_pass
if _currently_preprocessing_assets == 0:
_preprocess_second_pass()
Expand Down
Loading

0 comments on commit ba300db

Please sign in to comment.