1# ##### BEGIN GPL LICENSE BLOCK ##### 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, 9# but WITHOUT ANY WARRANTY; without even the implied warranty of 10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11# GNU General Public License for more details. 12# 13# You should have received a copy of the GNU General Public License 14# along with this program; if not, write to the Free Software Foundation, 15# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16# 17# ##### END GPL LICENSE BLOCK ##### 18 19# <pep8 compliant> 20from bl_ui.properties_animviz import ( 21 MotionPathButtonsPanel, 22 MotionPathButtonsPanel_display, 23) 24import bpy 25from bpy.types import Panel, Menu 26from rna_prop_ui import PropertyPanel 27 28 29class ObjectButtonsPanel: 30 bl_space_type = 'PROPERTIES' 31 bl_region_type = 'WINDOW' 32 bl_context = "object" 33 34 35class OBJECT_PT_context_object(ObjectButtonsPanel, Panel): 36 bl_label = "" 37 bl_options = {'HIDE_HEADER'} 38 39 def draw(self, context): 40 layout = self.layout 41 space = context.space_data 42 43 if space.use_pin_id: 44 layout.template_ID(space, "pin_id") 45 else: 46 row = layout.row() 47 row.template_ID(context.view_layer.objects, "active", filter='AVAILABLE') 48 49 50class OBJECT_PT_transform(ObjectButtonsPanel, Panel): 51 bl_label = "Transform" 52 53 def draw(self, context): 54 layout = self.layout 55 layout.use_property_split = True 56 57 ob = context.object 58 59 col = layout.column() 60 row = col.row(align=True) 61 row.prop(ob, "location") 62 row.use_property_decorate = False 63 row.prop(ob, "lock_location", text="", emboss=False, icon='DECORATE_UNLOCKED') 64 65 rotation_mode = ob.rotation_mode 66 if rotation_mode == 'QUATERNION': 67 col = layout.column() 68 row = col.row(align=True) 69 row.prop(ob, "rotation_quaternion", text="Rotation") 70 sub = row.column(align=True) 71 sub.use_property_decorate = False 72 sub.prop(ob, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') 73 sub.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') 74 elif rotation_mode == 'AXIS_ANGLE': 75 col = layout.column() 76 row = col.row(align=True) 77 row.prop(ob, "rotation_axis_angle", text="Rotation") 78 79 sub = row.column(align=True) 80 sub.use_property_decorate = False 81 sub.prop(ob, "lock_rotation_w", text="", emboss=False, icon='DECORATE_UNLOCKED') 82 sub.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') 83 else: 84 col = layout.column() 85 row = col.row(align=True) 86 row.prop(ob, "rotation_euler", text="Rotation") 87 row.use_property_decorate = False 88 row.prop(ob, "lock_rotation", text="", emboss=False, icon='DECORATE_UNLOCKED') 89 row = layout.row(align=True) 90 row.prop(ob, "rotation_mode", text="Mode") 91 row.label(text="", icon='BLANK1') 92 93 col = layout.column() 94 row = col.row(align=True) 95 row.prop(ob, "scale") 96 row.use_property_decorate = False 97 row.prop(ob, "lock_scale", text="", emboss=False, icon='DECORATE_UNLOCKED') 98 99 100class OBJECT_PT_delta_transform(ObjectButtonsPanel, Panel): 101 bl_label = "Delta Transform" 102 bl_parent_id = "OBJECT_PT_transform" 103 bl_options = {'DEFAULT_CLOSED'} 104 105 def draw(self, context): 106 layout = self.layout 107 layout.use_property_split = True 108 109 ob = context.object 110 111 col = layout.column() 112 col.prop(ob, "delta_location", text="Location") 113 114 rotation_mode = ob.rotation_mode 115 if rotation_mode == 'QUATERNION': 116 col.prop(ob, "delta_rotation_quaternion", text="Rotation") 117 elif rotation_mode == 'AXIS_ANGLE': 118 pass 119 else: 120 col.prop(ob, "delta_rotation_euler", text="Rotation") 121 122 col.prop(ob, "delta_scale", text="Scale") 123 124 125class OBJECT_PT_relations(ObjectButtonsPanel, Panel): 126 bl_label = "Relations" 127 bl_options = {'DEFAULT_CLOSED'} 128 129 def draw(self, context): 130 layout = self.layout 131 layout.use_property_split = True 132 flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) 133 134 ob = context.object 135 136 col = flow.column() 137 col.prop(ob, "parent") 138 sub = col.column() 139 sub.prop(ob, "parent_type") 140 parent = ob.parent 141 if parent and ob.parent_type == 'BONE' and parent.type == 'ARMATURE': 142 sub.prop_search(ob, "parent_bone", parent.data, "bones") 143 sub.active = (parent is not None) 144 145 col.separator() 146 147 col = flow.column() 148 149 col.prop(ob, "track_axis", text="Tracking Axis") 150 col.prop(ob, "up_axis", text="Up Axis") 151 152 col.separator() 153 154 col = flow.column() 155 156 col.prop(ob, "pass_index") 157 158 159class COLLECTION_MT_context_menu(Menu): 160 bl_label = "Collection Specials" 161 162 def draw(self, _context): 163 layout = self.layout 164 165 layout.operator("object.collection_unlink", icon='X') 166 layout.operator("object.collection_objects_select") 167 layout.operator("object.instance_offset_from_cursor") 168 169 170class OBJECT_PT_collections(ObjectButtonsPanel, Panel): 171 bl_label = "Collections" 172 bl_options = {'DEFAULT_CLOSED'} 173 174 def draw(self, context): 175 layout = self.layout 176 177 obj = context.object 178 179 row = layout.row(align=True) 180 if bpy.data.collections: 181 row.operator("object.collection_link", text="Add to Collection") 182 else: 183 row.operator("object.collection_add", text="Add to Collection") 184 row.operator("object.collection_add", text="", icon='ADD') 185 186 obj_name = obj.name 187 for collection in bpy.data.collections: 188 # XXX this is slow and stupid!, we need 2 checks, one that's fast 189 # and another that we can be sure its not a name collision 190 # from linked library data 191 collection_objects = collection.objects 192 if obj_name in collection.objects and obj in collection_objects[:]: 193 col = layout.column(align=True) 194 195 col.context_pointer_set("collection", collection) 196 197 row = col.box().row() 198 row.prop(collection, "name", text="") 199 row.operator("object.collection_remove", text="", icon='X', emboss=False) 200 row.menu("COLLECTION_MT_context_menu", icon='DOWNARROW_HLT', text="") 201 202 row = col.box().row() 203 row.prop(collection, "instance_offset", text="") 204 205 206class OBJECT_PT_display(ObjectButtonsPanel, Panel): 207 bl_label = "Viewport Display" 208 bl_options = {'DEFAULT_CLOSED'} 209 bl_order = 10 210 211 def draw(self, context): 212 layout = self.layout 213 layout.use_property_split = True 214 215 obj = context.object 216 obj_type = obj.type 217 is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'}) 218 is_wire = (obj_type in {'CAMERA', 'EMPTY'}) 219 is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE') 220 is_dupli = (obj.instance_type != 'NONE') 221 is_gpencil = (obj_type == 'GPENCIL') 222 223 col = layout.column(heading="Show") 224 col.prop(obj, "show_name", text="Name") 225 col.prop(obj, "show_axis", text="Axis") 226 227 # Makes no sense for cameras, armatures, etc.! 228 # but these settings do apply to dupli instances 229 if is_geometry or is_dupli: 230 col.prop(obj, "show_wire", text="Wireframe") 231 if obj_type == 'MESH' or is_dupli: 232 col.prop(obj, "show_all_edges", text="All Edges") 233 if is_geometry: 234 col.prop(obj, "show_texture_space", text="Texture Space") 235 col.prop(obj.display, "show_shadows", text="Shadow") 236 col.prop(obj, "show_in_front", text="In Front") 237 # if obj_type == 'MESH' or is_empty_image: 238 # col.prop(obj, "show_transparent", text="Transparency") 239 sub = layout.column() 240 if is_wire: 241 # wire objects only use the max. display type for duplis 242 sub.active = is_dupli 243 sub.prop(obj, "display_type", text="Display As") 244 245 if is_geometry or is_dupli or is_empty_image or is_gpencil: 246 # Only useful with object having faces/materials... 247 col.prop(obj, "color") 248 249 col = layout.column(align=False, heading="Bounds") 250 col.use_property_decorate = False 251 row = col.row(align=True) 252 sub = row.row(align=True) 253 sub.prop(obj, "show_bounds", text="") 254 sub = sub.row(align=True) 255 sub.active = obj.show_bounds or (obj.display_type == 'BOUNDS') 256 sub.prop(obj, "display_bounds_type", text="") 257 row.prop_decorator(obj, "display_bounds_type") 258 259 260class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): 261 bl_label = "Instancing" 262 bl_options = {'DEFAULT_CLOSED'} 263 264 def draw(self, context): 265 layout = self.layout 266 267 ob = context.object 268 269 row = layout.row() 270 row.prop(ob, "instance_type", expand=True) 271 272 layout.use_property_split = True 273 274 if ob.instance_type == 'VERTS': 275 layout.prop(ob, "use_instance_vertices_rotation", text="Align to Vertex Normal") 276 277 elif ob.instance_type == 'COLLECTION': 278 col = layout.column() 279 col.prop(ob, "instance_collection", text="Collection") 280 281 if ob.instance_type != 'NONE' or ob.particle_systems: 282 col = layout.column(heading="Show Instancer", align=True) 283 col.prop(ob, "show_instancer_for_viewport", text="Viewport") 284 col.prop(ob, "show_instancer_for_render", text="Render") 285 286 287class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel): 288 bl_label = "Scale by Face Size" 289 bl_parent_id = "OBJECT_PT_instancing" 290 291 @classmethod 292 def poll(cls, context): 293 ob = context.object 294 return ob.instance_type == 'FACES' 295 296 def draw_header(self, context): 297 298 ob = context.object 299 self.layout.prop(ob, "use_instance_faces_scale", text="") 300 301 def draw(self, context): 302 layout = self.layout 303 ob = context.object 304 layout.use_property_split = True 305 306 layout.active = ob.use_instance_faces_scale 307 layout.prop(ob, "instance_faces_scale", text="Factor") 308 309 310class OBJECT_PT_motion_paths(MotionPathButtonsPanel, Panel): 311 #bl_label = "Object Motion Paths" 312 bl_context = "object" 313 bl_options = {'DEFAULT_CLOSED'} 314 315 @classmethod 316 def poll(cls, context): 317 return (context.object) 318 319 def draw(self, context): 320 # layout = self.layout 321 322 ob = context.object 323 avs = ob.animation_visualization 324 mpath = ob.motion_path 325 326 self.draw_settings(context, avs, mpath) 327 328 329class OBJECT_PT_motion_paths_display(MotionPathButtonsPanel_display, Panel): 330 #bl_label = "Object Motion Paths" 331 bl_context = "object" 332 bl_parent_id = "OBJECT_PT_motion_paths" 333 bl_options = {'DEFAULT_CLOSED'} 334 335 @classmethod 336 def poll(cls, context): 337 return (context.object) 338 339 def draw(self, context): 340 # layout = self.layout 341 342 ob = context.object 343 avs = ob.animation_visualization 344 mpath = ob.motion_path 345 346 self.draw_settings(context, avs, mpath) 347 348 349class OBJECT_PT_visibility(ObjectButtonsPanel, Panel): 350 bl_label = "Visibility" 351 bl_options = {'DEFAULT_CLOSED'} 352 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 353 354 @classmethod 355 def poll(cls, context): 356 return (context.object) and (context.engine in cls.COMPAT_ENGINES) 357 358 def draw(self, context): 359 layout = self.layout 360 layout.use_property_split = True 361 362 layout = self.layout 363 ob = context.object 364 365 layout.prop(ob, "hide_select", text="Selectable", toggle=False, invert_checkbox=True) 366 367 col = layout.column(heading="Show in") 368 col.prop(ob, "hide_viewport", text="Viewports", toggle=False, invert_checkbox=True) 369 col.prop(ob, "hide_render", text="Renders", toggle=False, invert_checkbox=True) 370 371 if context.object.type == 'GPENCIL': 372 col = layout.column(heading="Grease Pencil") 373 col.prop(ob, "use_grease_pencil_lights", toggle=False) 374 375 376class OBJECT_PT_custom_props(ObjectButtonsPanel, PropertyPanel, Panel): 377 COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} 378 _context_path = "object" 379 _property_type = bpy.types.Object 380 381 382classes = ( 383 OBJECT_PT_context_object, 384 OBJECT_PT_transform, 385 OBJECT_PT_delta_transform, 386 OBJECT_PT_relations, 387 COLLECTION_MT_context_menu, 388 OBJECT_PT_collections, 389 OBJECT_PT_instancing, 390 OBJECT_PT_instancing_size, 391 OBJECT_PT_motion_paths, 392 OBJECT_PT_motion_paths_display, 393 OBJECT_PT_display, 394 OBJECT_PT_visibility, 395 OBJECT_PT_custom_props, 396) 397 398if __name__ == "__main__": # only for live edit. 399 from bpy.utils import register_class 400 for cls in classes: 401 register_class(cls) 402