1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
# SPDX-License-Identifier: GPL-2.0-or-later
"""
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)
|