Skip to content

Instantly share code, notes, and snippets.

@SuperFromND
Created April 6, 2024 22:52
Show Gist options
  • Save SuperFromND/c909b99a3f0c61dd4b096cbed732042b to your computer and use it in GitHub Desktop.
Save SuperFromND/c909b99a3f0c61dd4b096cbed732042b to your computer and use it in GitHub Desktop.
Blender Python: Cleanup Modnao Model Imports
# Script by SuperFromND. Last updated 4/6/2024
# This was written to clean up exported model data from ModNao, a Sega Dreamcast model ripping website.
# License: CC0/WTFPL/Unlicense/whatever else you prefer to mean "public domain".
# No warranty or liability, do whatever you want, yada yada etc.
import bpy
# ========================================================
# Change this value if you're getting weird results from the merge-by-distance.
# Higher values = more aggressive merging.
threshold_value = 0.01
# ========================================================
# holds names of image textures, used to check dupe materials
image_mats = []
# reset selected objects
bpy.ops.object.select_all(action="DESELECT")
print("Cleaning up materials...")
for obj in bpy.data.objects:
if obj.type == "MESH" and not obj.active_material == None:
for item in obj.material_slots:
mat = bpy.data.materials[item.name]
if mat.use_nodes:
mat.blend_method = "CLIP"
# first pass to just remove anything that's not necessary, i.e. we keep images and material out ONLY
for node in mat.node_tree.nodes:
if node.type == "TEX_IMAGE" or node.type == "OUTPUT_MATERIAL":
# do nothing, but we keep this node
pass
else:
mat.node_tree.nodes.remove(node)
# second pass to re-assemble the nodes into a normal material
pbsdf = mat.node_tree.nodes.new("ShaderNodeBsdfPrincipled")
matoutput = mat.node_tree.nodes.get("Material Output")
for node in mat.node_tree.nodes:
if node.type == "TEX_IMAGE":
links = mat.node_tree.links
links.new(node.outputs[0], pbsdf.inputs["Base Color"])
links.new(node.outputs[1], pbsdf.inputs["Alpha"])
links.new(pbsdf.outputs["BSDF"], matoutput.inputs["Surface"])
# checks for duplicate materials based on image texture name
if node.image.name in image_mats:
for mat2 in bpy.data.materials:
if mat2.name == node.image.name:
obj.data.materials[0] = mat2
else:
print(
"Unique image detected, creating material: "
+ node.image.name
)
mat.name = node.image.name
image_mats.append(node.image.name)
# now it's time to cleanup the mesh data
# loop over top-level objects
print("Cleaning up meshes...")
root_objs = (obj for obj in bpy.context.scene.objects if not obj.parent)
for obj in root_objs:
if obj.children == None:
continue
else:
for child in obj.children:
# this should be all the "object" nodes, which contain more empties and meshes respectively
obj_name = child.name
mesh_list = []
# fetch all meshes (assuming structure is consistent PLEASE BE CONSISTENT AAAAA)
for obj_empty in child.children:
for obj_mesh in obj_empty.children:
mesh_list.append(obj_mesh)
for objz in mesh_list:
objz.select_set(True)
# clear their parents but keep transforms
bpy.ops.object.parent_clear(type="CLEAR_KEEP_TRANSFORM")
# check to see if there's meshes
if len(mesh_list) == 0:
continue
else:
print("Merging " + obj_name + "'s meshes...")
# select first object in the mesh list and then join to that
bpy.context.view_layer.objects.active = mesh_list[0]
bpy.ops.object.join()
# deselect all selected objects so it doesnt just make one huge mesh
bpy.ops.object.select_all(action="DESELECT")
# final bit of cleanup: apply a merge-by-distance on every object and delete any remaining empties
for obj in bpy.data.objects:
if obj.type == "EMPTY":
bpy.data.objects.remove(obj)
elif obj.type == "MESH":
print("Applying merge-by-distance to " + obj.name + "...")
obj.select_set(True)
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type="VERT")
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.remove_doubles(
threshold=threshold_value,
use_unselected=False,
use_sharp_edge_from_normals=False,
)
obj.select_set(False)
# set back to object mode
bpy.ops.object.mode_set(mode="OBJECT")
print("Done. Have fun!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment