# 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 2 # 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 # MERCHANTABILITY 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, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ Object ID for Dupli Groups Say you have a linked character or asset, you can now set an Object ID for the entire instance (the objects in the group), and use it with the Object Index pass later in compositing. Something that I always wanted and it wasn't possible! In order for the Object ID to be loaded afterwards on computers without Amaranth installed, it will automatically create a text file (called AmaranthStartup.py) and save it inside the .blend, this will autorun on startup and set the OB IDs. Remember to have auto-run python scripts on your startup preferences. Set a Pass Index and press "Apply Object ID to Duplis" on the Relations panel, Object Properties. """ import bpy from amaranth.scene.debug import AMTH_SCENE_OT_blender_instance_open # Some settings are bound to be saved on a startup py file # TODO: refactor this, amth_text should not be declared as a global variable, # otherwise becomes confusing when you call it in the classes below. def amaranth_text_startup(context): amth_text_name = "AmaranthStartup.py" amth_text_exists = False global amth_text try: if bpy.data.texts: for tx in bpy.data.texts: if tx.name == amth_text_name: amth_text_exists = True amth_text = bpy.data.texts[amth_text_name] break else: amth_text_exists = False if not amth_text_exists: bpy.ops.text.new() amth_text = bpy.data.texts[((len(bpy.data.texts) * -1) + 1)] amth_text.name = amth_text_name amth_text.write("# Amaranth Startup Script\nimport bpy\n") amth_text.use_module = True return amth_text_exists except AttributeError: return None # FEATURE: Dupli Group Path def ui_dupli_group_library_path(self, context): ob = context.object row = self.layout.row() row.alignment = "LEFT" if ob and ob.instance_collection and ob.instance_collection.library: lib = ob.instance_collection.library.filepath row.operator(AMTH_SCENE_OT_blender_instance_open.bl_idname, text="Library: %s" % lib, emboss=False, icon="LINK_BLEND").filepath = lib # // FEATURE: Dupli Group Path # FEATURE: Object ID for objects inside DupliGroups class AMTH_OBJECT_OT_id_dupligroup(bpy.types.Operator): """Set the Object ID for objects in the dupli group""" bl_idname = "object.amaranth_object_id_duplis" bl_label = "Apply Object ID to Duplis" clear = False @classmethod def poll(cls, context): return context.active_object.instance_collection def execute(self, context): self.__class__.clear = False ob = context.active_object amaranth_text_startup(context) script_exists = False script_intro = "# OB ID: %s" % ob.name obdata = 'bpy.data.objects[" % s"]' % ob.name # TODO: cleanup script var using format or template strings script = "%s" % ( "\nif %(obdata)s and %(obdata)s.instance_collection and %(obdata)s.pass_index != 0: %(obname)s \n" " for dob in %(obdata)s.instance_collection.objects: %(obname)s \n" " dob.pass_index = %(obdata)s.pass_index %(obname)s \n" % {"obdata": obdata, "obname": script_intro}) for txt in bpy.data.texts: if txt.name == amth_text.name: for li in txt.lines: if script_intro == li.body: script_exists = True continue if not script_exists: amth_text.write("\n") amth_text.write(script_intro) amth_text.write(script) if ob and ob.instance_collection: if ob.pass_index != 0: for dob in ob.instance_collection.objects: dob.pass_index = ob.pass_index self.report({"INFO"}, "%s ID: %s to all objects in this Dupli Group" % ( "Applied" if not script_exists else "Updated", ob.pass_index)) return {"FINISHED"} class AMTH_OBJECT_OT_id_dupligroup_clear(bpy.types.Operator): """Clear the Object ID from objects in dupli group""" bl_idname = "object.amaranth_object_id_duplis_clear" bl_label = "Clear Object ID from Duplis" @classmethod def poll(cls, context): return context.active_object.instance_collection def execute(self, context): context.active_object.pass_index = 0 AMTH_OBJECT_OT_id_dupligroup.clear = True amth_text_exists = amaranth_text_startup(context) match_first = "# OB ID: %s" % context.active_object.name if amth_text_exists: for txt in bpy.data.texts: if txt.name == amth_text.name: for li in txt.lines: if match_first in li.body: li.body = "" continue self.report({"INFO"}, "Object IDs back to normal") return {"FINISHED"} def ui_object_id_duplis(self, context): if context.active_object.instance_collection: split = self.layout.split() row = split.row(align=True) row.enabled = context.active_object.pass_index != 0 row.operator( AMTH_OBJECT_OT_id_dupligroup.bl_idname) row.operator( AMTH_OBJECT_OT_id_dupligroup_clear.bl_idname, icon="X", text="") split.separator() if AMTH_OBJECT_OT_id_dupligroup.clear: self.layout.label(text="Next time you save/reload this file, " "object IDs will be back to normal", icon="INFO") # // FEATURE: Object ID for objects inside DupliGroups def register(): bpy.utils.register_class(AMTH_OBJECT_OT_id_dupligroup) bpy.utils.register_class(AMTH_OBJECT_OT_id_dupligroup_clear) bpy.types.OBJECT_PT_instancing.append(ui_dupli_group_library_path) bpy.types.OBJECT_PT_relations.append(ui_object_id_duplis) def unregister(): bpy.utils.unregister_class(AMTH_OBJECT_OT_id_dupligroup) bpy.utils.unregister_class(AMTH_OBJECT_OT_id_dupligroup_clear) bpy.types.OBJECT_PT_instancing.remove(ui_dupli_group_library_path) bpy.types.OBJECT_PT_relations.remove(ui_object_id_duplis)