diff --git a/README.md b/README.md index e7327b6..7d242ff 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ -# Simple-Material-Flattener - -This plugin looks through materials in selected objects, and if they're simple enough (a single texture) then it attempts to consolidate all materials sharing that texture and keep the faces that materials were assigned to. \ No newline at end of file +The user interface is created in the "right" panel in the 3D View +This panel is normally closed, so it must be opened (+) to be seen. + +I want to take ripped assets that have many duplicate materials +and combine them all into a single material. + +This plugin looks through materials in selected objects, and if they're simple enough (a single texture) then it attempts to consolidate all materials sharing that texture and keep the faces that materials were assigned to. + +This does NOT keep shader settings, and should be used on objects that are imported with default-ish materials like obj rips. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..1a565cf --- /dev/null +++ b/__init__.py @@ -0,0 +1,54 @@ +# /bin/python + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +#---------------------------------------------------------- +# File __init__.py +#---------------------------------------------------------- + + +bl_info = { + "name" : "Simple Material Flattener", + "author" : "Irssss sssssson", + "description" : "", + "blender" : (3, 3, 1), + "version" : (0, 0, 1), + "location" : "View3D > N Panel > Misc.", + "warning" : "", + "category" : "Material" +} + +if "bpy" in locals(): + from importlib import reload + reload(operators) + reload(panel) +else: + from . import operators + from . import panel + +import bpy +from bpy.types import Scene + +classes = ( + operators.SMF_OT_flatten_materials, + panel.SMF_PT_Panel + ) + +def register(): + for c in classes: + bpy.utils.register_class(c) + +def unregister(): + for c in reversed(classes): + bpy.utils.unregister_class(c) diff --git a/operators.py b/operators.py new file mode 100644 index 0000000..e075864 --- /dev/null +++ b/operators.py @@ -0,0 +1,99 @@ +import bpy +import bpy.ops +from bpy.types import Operator + +class SMF_OT_flatten_materials(Operator): + bl_idname = 'scene.flatten_materials' + bl_label = 'Flatten materials' + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + return True + + def execute(self, context): + # self.scene = context.scene + # bpy.context.selected_objects + # object.material_slots[0].material.node_tree.nodes (list) + # bpy.ops.object.material_slot_select() + # bpy.ops.object.mode_set(mode='EDIT') (mode='OBJECT) + # bpy.types.ShaderNodeTexImage() ??? + + selected_objects = [i for i in bpy.context.selected_objects] + store_selected_objects = selected_objects.copy() + last_image = None + + #select none + bpy.ops.object.select_all(action='DESELECT') + + for o in selected_objects: + imageMaterialNames = {} + print("gettings materials from %s" % o.name) + # select this object + o.select_set(True) + # switch to edit mode + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + + for slot in o.material_slots: + nodes = slot.material.node_tree.nodes + + # Check to not break more complex materials + if not self.CheckIsSimpleMaterial(nodes): + print("not simple enough") + continue + + image = self.getImage(nodes) + if not last_image: last_image = image + + if last_image == image: + o.active_material_index = slot.slot_index + bpy.ops.object.material_slot_select() + continue + + # materials are not the same: Finish up last image and start again + if last_image in imageMaterialNames: + print("re-assigning materials") + print("Hard Mode") + # select other material's mesh # active material index bpy.data.objects["name"].active_material_index : INT + o.active_material_index = o.material_slots[imageMaterialNames[last_image]].slot_index + # assign to this slot # bpy.ops.object.material_slot_assign() + bpy.ops.object.material_slot_assign() + else: + print("new texture path: %s" % last_image.filepath) + imageMaterialNames[last_image] = o.material_slots[o.active_material_index].name + bpy.ops.object.material_slot_assign() + bpy.ops.mesh.select_all(action='DESELECT') + last_image = image + + # select this material + o.active_material_index = slot.slot_index + # select this materials faces + bpy.ops.object.material_slot_select() + + bpy.ops.object.material_slot_assign() + # switch to object mode + bpy.ops.object.mode_set(mode='OBJECT') + # remove unused slots + bpy.ops.object.material_slot_remove_unused() + + # reselect all + for o in selected_objects: + o.select_set(True) + + return {'FINISHED'} + + def getImage(self, nodes): + for node in nodes: + if node.type == 'TEX_IMAGE': + return node.image + else: + return False + + def CheckIsSimpleMaterial(self, nodes): + if len(nodes) != 3: return False + else: return True + + + + diff --git a/panel.py b/panel.py new file mode 100644 index 0000000..7ce2da1 --- /dev/null +++ b/panel.py @@ -0,0 +1,13 @@ +from bpy.types import Panel + +class SMF_PT_Panel(Panel): + bl_idname = "VIEW3D_PT_simple_material_flattener_331" + bl_label = "Simple Material Flattener" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + + def draw(self, context): + layout = self.layout + row = layout.row() + col = row.column() + self.layout.operator("scene.flatten_materials", text="Flatten materials") \ No newline at end of file