diff --git a/.gitignore b/.gitignore index 5870ab2d3..7ddd5059c 100755 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,5 @@ expected/* # Doxygen docs/html/* +objects +tools/mingw64 diff --git a/.gitmodules b/.gitmodules index f5851947d..4daeb0793 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "doxygen-awesome-css"] path = doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git +[submodule "tools/fast64"] + path = tools/fast64 + url = https://github.com/Fast-64/fast64.git diff --git a/open_blender_fast64.bat b/open_blender_fast64.bat new file mode 100644 index 000000000..df72ffd83 --- /dev/null +++ b/open_blender_fast64.bat @@ -0,0 +1 @@ +"c:\Program Files\Blender Foundation\Blender 4.0\blender.exe" --python tools/blender_extension/fast64_run.py \ No newline at end of file diff --git a/test_blend.bat b/test_blend.bat new file mode 100644 index 000000000..a190f6fcd --- /dev/null +++ b/test_blend.bat @@ -0,0 +1 @@ +"c:\Program Files\Blender Foundation\Blender 4.0\blender.exe" -b --python tools/blender_extension/blender_export.py -- courses/dks_jungle_parkway/course_data.inc.c d_course_dks_jungle_parkway_dl_E058 \ No newline at end of file diff --git a/tools/3d_models.json b/tools/3d_models.json new file mode 100644 index 000000000..72dfa4c64 --- /dev/null +++ b/tools/3d_models.json @@ -0,0 +1,69 @@ +{ + "courses/dks_jungle_parkway/course_data.inc.c": [ + "d_course_dks_jungle_parkway_dl_E730", + "d_course_dks_jungle_parkway_dl_E058", + "d_course_dks_jungle_parkway_dl_FC28", + "d_course_dks_jungle_parkway_dl_kiwano_fruit", + "d_course_dks_jungle_parkway_dl_palm_tree", + "d_course_dks_jungle_parkway_dl_13878", + "d_course_dks_jungle_parkway_dl_tree3", + "d_course_dks_jungle_parkway_dl_tree2", + "d_course_dks_jungle_parkway_dl_tree1" + ], + "courses/banshee_boardwalk/course_data.inc.c": [ + "d_course_banshee_boardwalk_dl_B278", + "d_course_banshee_boardwalk_dl_trash_bin", + "d_course_banshee_boardwalk_dl_cheep_cheep" + ], + "courses/bowsers_castle/course_data.inc.c": [ + "d_course_bowsers_castle_dl_thwomp", + "d_course_bowsers_castle_dl_bush" + ], + "courses/choco_mountain/course_data.inc.c":[ + "d_course_choco_mountain_dl_falling_rock" + ], + "courses/frappe_snowland/course_data.inc.c":[ + "d_course_frappe_snowland_dl_tree" + ], + "courses/kalimari_desert/course_data.inc.c":[ + "d_course_kalimari_desert_dl_cactus1", + "d_course_kalimari_desert_dl_cactus2", + "d_course_kalimari_desert_dl_cactus3", + "d_course_kalimari_desert_dl_crossing_right_active", + "d_course_kalimari_desert_dl_crossing_left_active", + "d_course_kalimari_desert_dl_crossing_both_inactive", + "d_course_kalimari_desert_dl_1B978", + "d_course_kalimari_desert_dl_1C0F0", + "d_course_kalimari_desert_dl_1D160", + "d_course_kalimari_desert_dl_1D670", + "d_course_kalimari_desert_dl_1E480", + "d_course_kalimari_desert_dl_1E910", + "d_course_kalimari_desert_dl_1F228", + "d_course_kalimari_desert_dl_1F708", + "d_course_kalimari_desert_dl_1FAF8", + "d_course_kalimari_desert_dl_20A20", + "d_course_kalimari_desert_dl_21220", + "d_course_kalimari_desert_dl_21550", + "d_course_kalimari_desert_dl_21A80", + "d_course_kalimari_desert_dl_21C90", + "d_course_kalimari_desert_dl_22D28", + "d_course_kalimari_desert_dl_22D70", + "d_course_kalimari_desert_dl_22DB8" + ], + "courses/koopa_troopa_beach/course_data.inc.c":[ + "d_course_koopa_troopa_beach_dl_16630", + "d_course_koopa_troopa_beach_dl_tree_top1", + "d_course_koopa_troopa_beach_dl_tree_trunk1", + "d_course_koopa_troopa_beach_dl_tree_top2", + "d_course_koopa_troopa_beach_dl_tree_trunk2", + "d_course_koopa_troopa_beach_dl_tree_top3", + "d_course_koopa_troopa_beach_dl_tree_trunk3" + ], + "courses/luigi_raceway/course_data.inc.c": [ + "d_course_luigi_raceway_dl_F650", + "d_course_luigi_raceway_dl_F960", + "d_course_luigi_raceway_dl_FA20", + "d_course_luigi_raceway_dl_FBE0", + "d_course_luigi_raceway_dl_FC70" + ] +} \ No newline at end of file diff --git a/tools/actualise_type/main.py b/tools/actualise_type/main.py deleted file mode 100644 index 9f1246a93..000000000 --- a/tools/actualise_type/main.py +++ /dev/null @@ -1,147 +0,0 @@ -import os -import re -import argparse - -parser = argparse.ArgumentParser(description='Actualise type in C code') -parser.add_argument('-k', '--know_the_unknow', action='store_true', help='replace ? by type if is found somewhere') -parser.add_argument('-cv', "--change_var_name", action='store_true', help='change var name') -parser.add_argument("-cf", "--change_type_var", action='store_true', help="change type of var") - -arg = parser.parse_args() - -root = "." - -var:dict[str,str] = {} -number_type_var = {} -ban_type = [ - "extern", - "struct", - "static", - "UNUSED", - "?", - "include", - "const", - "case", - "ALIGNED8", - "typedef" -] -static = {} - -def check_file_and_folder(path:str, name:str) -> bool: - if not (name.endswith(".c") or name.endswith(".h")): - return True - - if "tools" in path: - return True - - return False - -def replace_type(var_name: str, var_type: str, chn: str) -> str: - if var_name in chn: - result = re.sub(rf"(?! )\w+ {var_name}", f"{var_type} {var_name}", chn) - result = re.sub(rf"(?! )\? {var_name}", f"{var_type} {var_name}", chn) - else: - result = chn - return result - -def find_type(prefix = ""): - for path, subdirs, files in os.walk(root): - for name in files: - if check_file_and_folder(path, name): - continue - - print(os.path.join(path, name)) - - with open(os.path.join(path, name)) as f: - data = f.read() - - if prefix=="": - result = re.findall(r'\n(\w+) (\w+)', data) - else: - result = re.findall(r'\n'+prefix+r' (\w+) (\w+)', data) - - for i in result: - - if i[1] == "//" or (i[0] in ban_type) or len(i[1])<1: - continue - - var[i[1]] = i[0] - - if i[0] in number_type_var: - number_type_var[i[0]] += 1 - else: - number_type_var[i[0]] = 1 - -def know_the_unknow(): - find_type() - - find_type("static") - - find_type("const") - - find_type("struct") - - find_type("extern") - - print(var) - - for path, subdirs, files in os.walk(root): - for name in files: - if check_file_and_folder(path, name): - continue - - print(os.path.join(path, name)) - with open(os.path.join(path, name), "r+") as f: - data = f.read() - for i in var: - data = replace_type(i, var[i], data) - - f.seek(0) - f.write(data) - -def change_var_name(): - - old_name = input("Old name: ") - new_name = input("New name: ") - - for path, subdirs, files in os.walk(root): - for name in files: - if check_file_and_folder(path, name): - continue - - print(os.path.join(path, name)) - with open(os.path.join(path, name), "r+") as f: - data = f.read() - data = data.replace(old_name, new_name) - - f.seek(0) - f.write(data) - -def change_type_var(): - name_var = input("Name var: ") - new_type = input("New type: ") - - for path, subdirs, files in os.walk(root): - for name in files: - if check_file_and_folder(path, name): - continue - - print(os.path.join(path, name)) - with open(os.path.join(path, name), "r+") as f: - data = f.read() - data = replace_type(name_var, new_type, data) - - f.seek(0) - f.write(data) - -def main(): - if arg.know_the_unknow: - print("actualise type") - know_the_unknow() - elif arg.change_var_name: - change_var_name() - elif arg.change_type_var: - change_type_var() - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/tools/blender_extension/blender_export.py b/tools/blender_extension/blender_export.py new file mode 100644 index 000000000..693002e4e --- /dev/null +++ b/tools/blender_extension/blender_export.py @@ -0,0 +1,51 @@ +import bpy +import os +import sys +sys.path.append('tools') +import fast64 +from fast64.fast64_internal.f3d.f3d_gbi import get_F3D_GBI +from fast64.fast64_internal.f3d.f3d_material import createF3DMat +from fast64.fast64_internal.f3d.f3d_parser import F3DContext, getImportData, importMeshC + +from blender_extension.clean_scene import clean_scene + +dir_path = os.path.dirname(os.path.realpath(__file__)) +dir_path = dir_path.replace("\\", "/") +dir_path = os.path.dirname(dir_path) +dir_path = os.path.dirname(dir_path) + +print(dir_path) + +fast64.register() + +argv = sys.argv +argv = argv[argv.index("--") + 1:] # get all args after "--" + +bpy.context.scene.f3d_type = "F3DEX/LX" +basePath = bpy.path.abspath(dir_path) +scaleValue = bpy.context.scene.blenderF3DScale +removeDoubles = bpy.context.scene.DLRemoveDoubles +importNormals = bpy.context.scene.DLImportNormals +drawLayer = bpy.context.scene.DLImportDrawLayer + +file = argv[0] +obj = argv[1] + +clean_scene() +if bpy.context.mode != "OBJECT": + bpy.ops.object.mode_set(mode="OBJECT") + +importPath = bpy.path.abspath(dir_path+"/"+file) + +data = getImportData([importPath]) + +importMeshC( + data, + obj, + scaleValue, + removeDoubles, + importNormals, + drawLayer, + F3DContext(get_F3D_GBI(), basePath, createF3DMat(None)), +) +bpy.ops.wm.save_as_mainfile(filepath="objects/"+obj+".blend") \ No newline at end of file diff --git a/tools/blender_extension/clean_scene.py b/tools/blender_extension/clean_scene.py new file mode 100644 index 000000000..2e054b6c0 --- /dev/null +++ b/tools/blender_extension/clean_scene.py @@ -0,0 +1,43 @@ +import bpy + +def purge_orphans(): + if bpy.app.version >= (3, 0, 0): + bpy.ops.outliner.orphans_purge( + do_local_ids=True, do_linked_ids=True, do_recursive=True + ) + else: + # call purge_orphans() recursively until there are no more orphan data blocks to purge + result = bpy.ops.outliner.orphans_purge() + if result.pop() != "CANCELLED": + purge_orphans() + + +def clean_scene(): + """ + Removing all of the objects, collection, materials, particles, + textures, images, curves, meshes, actions, nodes, and worlds from the scene + """ + if bpy.context.active_object and bpy.context.active_object.mode == "EDIT": + bpy.ops.object.editmode_toggle() + + for obj in bpy.data.objects: + obj.hide_set(False) + obj.hide_select = False + obj.hide_viewport = False + + bpy.ops.object.select_all(action="SELECT") + bpy.ops.object.delete() + + collection_names = [col.name for col in bpy.data.collections] + for name in collection_names: + bpy.data.collections.remove(bpy.data.collections[name]) + + # in the case when you modify the world shader + world_names = [world.name for world in bpy.data.worlds] + for name in world_names: + bpy.data.worlds.remove(bpy.data.worlds[name]) + # create a new world data block + bpy.ops.world.new() + bpy.context.scene.world = bpy.data.worlds["World"] + + purge_orphans() \ No newline at end of file diff --git a/tools/blender_extension/fast64_run.py b/tools/blender_extension/fast64_run.py new file mode 100644 index 000000000..7777cd557 --- /dev/null +++ b/tools/blender_extension/fast64_run.py @@ -0,0 +1,4 @@ +import sys +sys.path.append('tools') +import fast64 +fast64.register() \ No newline at end of file diff --git a/tools/extract_models.py b/tools/extract_models.py new file mode 100644 index 000000000..cbd58991c --- /dev/null +++ b/tools/extract_models.py @@ -0,0 +1,30 @@ +import json +import subprocess +import os + +if not os.path.exists("objects"): + os.makedirs("objects") + +models = json.load(open("tools/3d_models.json")) + +blender = "c:/Program Files/Blender Foundation/Blender 4.0/blender.exe" + +args = [ + "--background", + "--python", + "tools/blender_extension/blender_export.py", + "--", +] + +for file in models: + for obj in models[file]: + if os.path.exists("objects/"+obj+".blend"): + continue + print(f"Exporting {obj} from {file}") + args.append(file) + args.append(obj) + if subprocess.run([blender, *args]).returncode != 0 or not os.path.exists("objects/"+obj+".blend"): + print(f"Failed to export {obj} from {file}") + exit(1) + args.pop() + args.pop() \ No newline at end of file diff --git a/tools/fast64 b/tools/fast64 new file mode 160000 index 000000000..3d6f73097 --- /dev/null +++ b/tools/fast64 @@ -0,0 +1 @@ +Subproject commit 3d6f73097482b9db460f4cb7737d7624a7bf6362