-
Notifications
You must be signed in to change notification settings - Fork 3
/
3dcam-engine-helper.py
185 lines (172 loc) · 7.55 KB
/
3dcam-engine-helper.py
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
import bpy
from bpy.props import IntProperty, StringProperty, PointerProperty, BoolProperty
from bpy.types import PropertyGroup
from bpy.app.handlers import persistent
bl_info = {
"name": "3Dcam engine custom properties helper",
"author": "Schnappy",
"blender": (2,79,0),
"category": "Property helper",
"version": (0,0,1),
"location": "3D view > Object",
"location": "Properties panel > Data",
"description": "Set/Unset/Copy flags easily"
}
# Based on https://blog.hamaluik.ca/posts/dynamic-blender-properties by Kenton Hamaluik
bpy.propertyGroupLayouts = {
"Flags": [
{ "name": "isAnim", "type": "boolean" },
{ "name": "isProp", "type": "boolean" },
{ "name": "isRigidBody", "type": "boolean" },
{ "name": "isStaticBody","type": "boolean" },
{ "name": "isRound", "type": "boolean" },
{ "name": "isPrism", "type": "boolean" },
{ "name": "isActor", "type": "boolean" },
{ "name": "isLevel", "type": "boolean" },
{ "name": "isWall", "type": "boolean" },
{ "name": "isBG", "type": "boolean" },
{ "name": "isSprite", "type": "boolean" },
{ "name": "isLerp", "type": "boolean" },
{ "name": "isXA", "type": "boolean" }
],
"Others": [
{ "name": "mass", "type": "int" }
]
}
# store keymaps here to access after registration
addon_keymaps = []
bpy.samplePropertyGroups = {}
last_selection = []
store_att_names = []
def getActiveObjProps(active_obj):
object_custom_props = [prop for prop in store_att_names if prop in active_obj.data]
return object_custom_props
def menu_func(self, context):
self.layout.operator(copyCustomPropToSelection.bl_idname)
def copyCustomProps(context):
# get active object
active_obj = bpy.context.active_object
# get active object's custom properties
active_obj_custom_props = getActiveObjProps(active_obj)
# get selected objects
selection = bpy.context.selected_objects
# discriminates against active_obj
selection = [obj for obj in selection if obj != active_obj]
# for each object that's not active object, add custom prop
for obj in selection:
for prop in active_obj_custom_props:
obj.data[prop] = active_obj.data[prop]
def updateCustomProps(self, context):
global last_selection
global store_att_names
for att in store_att_names:
if (att in last_selection.Flags): # and last_selection.Flags[att] :
if ( att not in last_selection.data or
last_selection.Flags[att] != last_selection.data[att] ):
last_selection.data[att] = last_selection.Flags[att]
if (att in last_selection.Others):
if ( att not in last_selection.data or
last_selection.Others[att] != last_selection.data[att] ):
last_selection.data[att] = last_selection.Others[att]
@persistent
def selection_callback(scene):
global last_selection
global store_att_names
if bpy.context.active_object != last_selection:
last_selection = bpy.context.active_object
for groupName, attributeDefinitions in bpy.propertyGroupLayouts.items():
# build the attribute dictionary for this group
attributes = {}
for attributeDefinition in attributeDefinitions:
attType = attributeDefinition['type']
attName = attributeDefinition['name']
store_att_names.append(attName)
value = 0
if last_selection:
if attName in last_selection.data:
value = last_selection.data[attName]
if attType == 'boolean':
attributes[attName] = BoolProperty(name=attName, default=value, update=updateCustomProps )
elif attType == 'int':
attributes[attName] = IntProperty(name=attName, default=value, update=updateCustomProps)
elif attType == 'string':
attributes[attName] = StringProperty(name=attName, default=value, update=updateCustomProps)
else:
raise TypeError('Unsupported type (%s) for %s on %s!' % (attType, attName, groupName))
# now build the property group class
propertyGroupClass = type(groupName, (PropertyGroup,), attributes)
# register it with Blender
bpy.utils.register_class(propertyGroupClass)
# apply it to all Objects
setattr(bpy.types.Object, groupName, PointerProperty(type=propertyGroupClass))
# store it for later
bpy.samplePropertyGroups[groupName] = propertyGroupClass
class copyCustomPropToSelection(bpy.types.Operator):
"""Copy last selected object's custom properties to other selected objects"""
bl_idname = "object.copy_custom_properties"
bl_label = "Copy custom properties to selection"
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
copyCustomProps(context)
return {'FINISHED'}
class customPropsPanel(bpy.types.Panel):
bl_label = "3D Cam engine custom properties"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "data"
def draw(self, context):
layout = self.layout
obj = context.object
# use our layout definition to dynamically create our panel items
for groupName, attributeDefinitions in sorted(bpy.propertyGroupLayouts.items()):
# get the instance of our group
# dynamic equivalent of `obj.samplePropertyGroup` from before
propertyGroup = getattr(obj, groupName)
# start laying this group out
col = layout.column_flow(columns=2)
col.label(groupName)
i = 0
# loop through all the attributes and show them
for attributeDefinition in attributeDefinitions:
if i == len(attributeDefinitions)/2:
col.label("")
col.prop(propertyGroup, attributeDefinition["name"])
i += 1
# draw a separation between groups
layout.separator()
def register():
# register the panel class
bpy.utils.register_class(customPropsPanel)
bpy.app.handlers.scene_update_post.clear()
bpy.app.handlers.scene_update_post.append(selection_callback)
# copy helper
bpy.utils.register_class(copyCustomPropToSelection)
bpy.types.VIEW3D_MT_object.append(menu_func)
# shortcut
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new(copyCustomPropToSelection.bl_idname, 'P', 'PRESS', ctrl=False, shift=True)
# ~ kmi.properties.total = 4
addon_keymaps.append((km, kmi))
def unregister():
# unregister the panel class
bpy.utils.unregister_class(customPropsPanel)
# unregister our components
try:
for key, value in bpy.samplePropertyGroups.items():
delattr(bpy.types.Object, key)
bpy.utils.unregister_class(value)
except UnboundLocalError:
pass
bpy.samplePropertyGroups = {}
# copy helper
bpy.utils.unregister_class(copyCustomPropToSelection)
bpy.types.VIEW3D_MT_object.remove(menu_func)
# handle the keymap
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
if __name__ == "__main__":
register()