diff options
Diffstat (limited to 'release/scripts/startup/bl_ui/properties_object.py')
-rw-r--r-- | release/scripts/startup/bl_ui/properties_object.py | 523 |
1 files changed, 522 insertions, 1 deletions
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index b6a2792acd4..cba1454086c 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -18,8 +18,9 @@ # <pep8 compliant> import bpy -from bpy.types import Panel, Menu +from bpy.types import Panel, Menu, UIList from rna_prop_ui import PropertyPanel +from bl_ui.properties_physics_common import effector_weights_ui class ObjectButtonsPanel: @@ -261,6 +262,62 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel): row.prop(obj, "show_wire_color", text="", toggle=True, icon='WIRE') +# XXX temporary solution +bpy.types.CacheLibrary.filter_string = \ + bpy.props.StringProperty( + name="Filter Object Name", + description="Filter cache library objects by name", + ) +bpy.types.CacheLibrary.show_metadata = \ + bpy.props.BoolProperty( + name="Show Metadata", + description="Show metadata for the input archive", + default=False, + ) + + +def cachelib_objects(cachelib, group): + if not cachelib or not group: + return [] + + filter_string = cachelib.filter_string.lower() + if filter_string: + return filter(lambda ob: filter_string in ob.name.lower(), group.objects) + else: + return group.objects + +# Yields (type, index, enabled) +def cachelib_object_items(cachelib, ob): + filter_types = cachelib.data_types + + def items_desc(): + yield 'OBJECT', -1 + + if (ob.type == 'MESH'): + yield 'DERIVED_MESH', -1 + + for index, psys in enumerate(ob.particle_systems): + if psys.settings.type == 'EMITTER': + yield 'PARTICLES', index + if psys.settings.type == 'HAIR': + yield 'HAIR', index + yield 'HAIR_PATHS', index + + for datatype, index in items_desc(): + show = False + enable = False + # always show selected types + if datatype in filter_types: + show = True + enable = True + # special case: OBJECT type used as top level, show but disable + elif datatype == 'OBJECT': + show = True + enable = False + + if show: + yield datatype, index, enable + class OBJECT_PT_duplication(ObjectButtonsPanel, Panel): bl_label = "Duplication" @@ -298,6 +355,470 @@ class OBJECT_PT_duplication(ObjectButtonsPanel, Panel): layout.prop(ob, "dupli_group", text="Group") + +class CACHELIB_MT_shape_key_specials(Menu): + bl_label = "Shape Key Specials" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + + def draw(self, context): + layout = self.layout + + #layout.operator("object.shape_key_transfer", icon='COPY_ID') # icon is not ideal + #layout.operator("object.join_shapes", icon='COPY_ID') # icon is not ideal + layout.operator("cachelibrary.shape_key_add", icon='ZOOMIN', text="New Shape From Mix").from_mix = True + layout.operator("cachelibrary.shape_key_remove", icon='X', text="Delete All Shapes").all = True + layout.operator("cachelibrary.shape_key_move", icon='TRIA_UP_BAR', text="Move To Top").type = 'TOP' + layout.operator("cachelibrary.shape_key_move", icon='TRIA_DOWN_BAR', text="Move To Bottom").type = 'BOTTOM' + + +class CACHELIB_UL_shape_keys(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.ShapeKey)) + md = active_data + # key = data + key_block = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + split = layout.split(0.66, False) + split.prop(key_block, "name", text="", emboss=False, icon_value=icon) + row = split.row(align=True) + if key_block.mute: + row.active = False + if not item.id_data.use_relative: + row.prop(key_block, "frame", text="", emboss=False) + elif index > 0: + row.prop(key_block, "value", text="", emboss=False) + else: + row.label(text="") + row.prop(key_block, "mute", text="", emboss=False) + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class CACHELIBRARY_MT_hair_simulation_presets(Menu): + bl_label = "Hair Simulation Presets" + preset_subdir = "cachelibrary_hair_simulation" + preset_operator = "script.execute_preset" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + draw = Menu.draw_preset + + +class CacheArchiveInfoPanel(): + def draw_node_structure(self, context, layout, node, indent): + row = layout.row() + for i in range(indent): + row.label(text="", icon='BLANK1') + + if not node.child_nodes: + row.label(text="", icon='DOT') + elif not node.expand: + row.prop(node, "expand", text="", icon='DISCLOSURE_TRI_RIGHT', icon_only=True, emboss=False) + else: + row.prop(node, "expand", text="", icon='DISCLOSURE_TRI_DOWN', icon_only=True, emboss=False) + + for child in node.child_nodes: + self.draw_node_structure(context, layout, child, indent + 1) + + + info_columns = ['Name', 'Node', 'Samples', 'Size', 'Data', '', 'Array Size'] + + def draw_node_info(self, context, layout, node, column): + if column == 0: + layout.prop(node, "name", text="") + if column == 1: + layout.prop(node, "type", text="") + if column == 2: + if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}: + layout.prop(node, "samples", text="") + else: + layout.label(" ") + if column == 3: + size = int(node.bytes_size) + layout.label(sizeof_fmt(size) if size >= 0 else "-") + if column == 4: + if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}: + layout.prop(node, "datatype", text="") + else: + layout.label(" ") + if column == 5: + if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}: + layout.prop(node, "datatype_extent", text="") + else: + layout.label(" ") + if column == 6: + if node.type in {'ARRAY_PROPERTY'}: + layout.label(node.array_size if node.array_size >= 0 else "-") + else: + layout.label(" ") + + if node.expand: + for child in node.child_nodes: + self.draw_node_info(context, layout, child, column) + + def draw_info(self, context, layout, info, metadata=None): + row = layout.row(align=True) + row.label("Created by: {} | {}".format(info.app_name if info.app_name else "-", info.date_written if info.date_written else "-")) + if info.description: + layout.label(info.description) + + if metadata: + row = layout.row(align=True) + col_key = row.column() + col_value = row.column() + for key, value in metadata.items(): + col_key.label(key) + col_value.label(str(value)) + + if info.root_node: + row = layout.row() + + col = row.column() + col.label(" ") + self.draw_node_structure(context, col, info.root_node, 0) + + for i, column in enumerate(self.info_columns): + col = row.column() + col.label(column) + self.draw_node_info(context, col, info.root_node, i) + + +class OBJECT_PT_cache_library(CacheArchiveInfoPanel, ObjectButtonsPanel, Panel): + bl_label = "Cache" + + @classmethod + def poll(cls, context): + ob = context.object + return (ob and ob.dupli_type == 'GROUP' and ob.dupli_group) + + def draw_cache_modifier(self, context, layout, cachelib, md): + layout.context_pointer_set("cache_library", cachelib) + layout.context_pointer_set("cache_modifier", md) + + row = layout.row(align=True) + row.context_pointer_set("cache_modifier", md) + row.prop(md, "name", text="") + row.operator("cachelibrary.remove_modifier", icon='X', text="", emboss=False) + + # match enum type to our functions, avoids a lookup table. + getattr(self, md.type)(context, layout, cachelib, md) + + def draw_cachelib(self, context, layout, ob, cachelib, objects): + box = layout.box() + row = box.row() + row.label("Source:") + row.prop(cachelib, "source_mode", text="Source", expand=True) + row = box.row(align=True) + row.enabled = (cachelib.source_mode == 'CACHE') + row.prop(cachelib, "input_filepath", text="") + row.prop(cachelib, "show_metadata", text="", icon='QUESTION', toggle=True) + if cachelib.show_metadata and cachelib.archive_info: + self.draw_info(context, box, cachelib.archive_info, cachelib['input_metadata']) + + layout.separator() + + layout.prop(cachelib, "display_mode", expand=True) + row = layout.row() + split = row.split() + col = split.column() + col.label("Display:") + col.prop(cachelib, "display_motion", text="Motion") + col.prop(cachelib, "display_children", text="Children") + + layout.separator() + + row = layout.row(align=True) + row.enabled = (cachelib.display_mode == 'RESULT') + row.prop(cachelib, "output_filepath", text="") + + box = layout.box() + row = box.row() + + col = row.column() + row2 = col.row() + row2.alignment = 'LEFT' + row2.prop(cachelib, "data_types", icon_only=True, toggle=True) + row2.template_ID(cachelib, "filter_group") + col.prop(cachelib, "description") + col = row.column() + props = col.operator("cachelibrary.bake", text="Bake Preview", icon='RESTRICT_VIEW_OFF') + props.eval_mode = {'PREVIEW'} + if context.scene.use_preview_range: + props.start_frame = context.scene.frame_preview_start + props.end_frame = context.scene.frame_preview_end + else: + props.start_frame = context.scene.frame_start + props.end_frame = context.scene.frame_end + props = col.operator("cachelibrary.bake", text="Bake Render", icon='RESTRICT_RENDER_OFF') + props.eval_mode = {'RENDER'} + props.start_frame = context.scene.frame_start + props.end_frame = context.scene.frame_end + + ''' + row = layout.row(align=True) + row.label("Filter:") + row.prop(cachelib, "filter_string", icon='VIEWZOOM', text="") + + first = True + for ob in objects: + if not any(cachelib_object_items(cachelib, ob)): + continue + + if first: + layout.separator() + first = False + + for datatype, index, enable in cachelib_object_items(cachelib, ob): + row = layout.row(align=True) + row.alignment = 'LEFT' + row.template_cache_library_item(cachelib, ob, datatype, index, enable) + ''' + + layout.operator_menu_enum("cachelibrary.add_modifier", "type") + + for md in cachelib.modifiers: + box = layout.box() + self.draw_cache_modifier(context, box, cachelib, md) + + def draw(self, context): + ob = context.object + + layout = self.layout + row = layout.row(align=True) + row.template_ID(ob, "cache_library", new="cachelibrary.new") + + if ob.cache_library: + cache_objects = cachelib_objects(ob.cache_library, ob.dupli_group) + self.draw_cachelib(context, layout, ob, ob.cache_library, cache_objects) + + def HAIR_SIMULATION(self, context, layout, cachelib, md): + params = md.parameters + + col = layout.column(align=True) + col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA') + sub = col.column() + if (md.object): + sub.prop_search(md, "hair_system", md.object, "particle_systems") + else: + sub.enabled = False + sub.prop(md, "hair_system") + + layout = layout.column() + layout.active = md.hair_system is not None + + row = layout.row(align=True) + row.menu("CACHELIBRARY_MT_hair_simulation_presets", text=bpy.types.CACHELIBRARY_MT_hair_simulation_presets.bl_label) + props = row.operator("cachelibrary.hair_simulation_preset_add", text="", icon='ZOOMIN') + props.modifier_name = md.name + props = row.operator("cachelibrary.hair_simulation_preset_add", text="", icon='ZOOMOUT') + props.modifier_name = md.name + props.remove_active = True + + col = layout.column() + col.prop(params, "substeps") + col.prop(params, "timescale") + col.prop(params, "mass") + col.prop(params, "drag") + + row = col.row(align=True) + row.prop(params, "stretch_stiffness") + row.prop(params, "stretch_damping") + + row = col.row(align=True) + row.prop(params, "bend_stiffness") + row.prop(params, "bend_damping") + row.prop(params, "use_bend_stiffness_curve") + if params.use_bend_stiffness_curve: + sub = col.column() + sub.template_curve_mapping(params, "bend_stiffness_curve") + + row = col.row(align=True) + row.prop(params, "goal_stiffness") + row.prop(params, "goal_damping") + row = col.row(align=True) + row.prop(params, "use_goal_stiffness_curve") + row.prop(params, "use_goal_deflect") + if params.use_goal_stiffness_curve: + sub = col.column() + sub.template_curve_mapping(params, "goal_stiffness_curve") + + layout.separator() + + effector_weights_ui(self, context, params.effector_weights, 'HAIR') + + def FORCE_FIELD(self, context, layout, cachelib, md): + layout.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA') + + layout.prop(md, "force_type", text="") + + row = layout.row(align=True) + row.prop(md, "strength") + row.prop(md, "falloff") + + col = layout.column(align=True) + row = layout.row(align=True) + row.prop(md, "min_distance") + row.prop(md, "max_distance") + col.prop(md, "use_double_sided") + + def HAIRCUT(self, context, layout, cachelib, md): + col = layout.column(align=True) + col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA') + sub = col.column() + if (md.object): + sub.prop_search(md, "hair_system", md.object, "particle_systems") + else: + sub.enabled = False + sub.prop(md, "hair_system") + + row = layout.row() + row.prop_search(md, "target", context.blend_data, "objects", icon='OBJECT_DATA') + row.prop(md, "use_internal_target", text="Internal") + layout.prop(md, "cut_mode", toggle=True, expand=True) + + layout = layout.column() + layout.active = md.hair_system is not None + + def SHRINK_WRAP(self, context, layout, cachelib, md): + col = layout.column(align=True) + col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA') + sub = col.column() + if (md.object): + sub.prop_search(md, "hair_system", md.object, "particle_systems") + else: + sub.enabled = False + sub.prop(md, "hair_system") + + row = layout.row() + row.prop_search(md, "target", context.blend_data, "objects", icon='OBJECT_DATA') + row.prop(md, "use_internal_target", text="Internal") + + layout = layout.column() + layout.active = md.hair_system is not None + + def STRANDS_KEY(self, context, layout, cachelib, md): + col = layout.column(align=True) + col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA') + sub = col.column() + if (md.object): + sub.prop_search(md, "hair_system", md.object, "particle_systems") + else: + sub.enabled = False + sub.prop(md, "hair_system") + + key = md.shape_keys + kb = md.active_shape_key + kb_index = md.active_shape_key_index + + row = layout.row() + + rows = 2 + if kb: + rows = 4 + row.template_list("CACHELIB_UL_shape_keys", "", key, "key_blocks", md, "active_shape_key_index", rows=rows) + + col = row.column() + + sub = col.column(align=True) + #sub.operator("object.shape_key_add", icon='ZOOMIN', text="").from_mix = False + #sub.operator("object.shape_key_remove", icon='ZOOMOUT', text="").all = False + sub.menu("CACHELIB_MT_shape_key_specials", icon='DOWNARROW_HLT', text="") + + col.prop(md, "use_motion_state") + + if kb: + col.separator() + + sub = col.column(align=True) + #sub.operator("object.shape_key_move", icon='TRIA_UP', text="").type = 'UP' + #sub.operator("object.shape_key_move", icon='TRIA_DOWN', text="").type = 'DOWN' + + split = layout.split(percentage=0.4) + row = split.row() + row.prop(key, "use_relative") + + row = split.row() + row.alignment = 'RIGHT' + + sub = row.row(align=True) + sub.label() # XXX, for alignment only + subsub = sub.row(align=True) + subsub.prop(md, "show_only_shape_key", text="") + + sub = row.row() + #if key.use_relative: + # sub.operator("object.shape_key_clear", icon='X', text="") + #else: + # sub.operator("object.shape_key_retime", icon='RECOVER_LAST', text="") + + if key.use_relative: + if kb_index != 0: + row = layout.row() + row.prop(kb, "value") + + split = layout.split() + + col = split.column(align=True) + col.label(text="Range:") + col.prop(kb, "slider_min", text="Min") + col.prop(kb, "slider_max", text="Max") + + col = split.column(align=True) + col.label(text="Blend:") + #col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="") + col.prop_search(kb, "relative_key", key, "key_blocks", text="") + + else: + layout.prop(kb, "interpolation") + row = layout.column() + row.prop(key, "eval_time") + + +# Simple human-readable size (based on http://stackoverflow.com/a/1094933) +def sizeof_fmt(num, suffix='B'): + for unit in ['','K','M','G','T','P','E','Z']: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Y', suffix) + +class OBJECT_PT_cache_archive_info(CacheArchiveInfoPanel, ObjectButtonsPanel, Panel): + bl_label = "Cache Archive Info" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + ob = context.object + return (ob and ob.dupli_type == 'GROUP' and ob.dupli_group and ob.cache_library) + + def draw(self, context): + ob = context.object + cachelib = ob.cache_library + info = cachelib.archive_info + + layout = self.layout + + row = layout.row() + props = row.operator("cachelibrary.archive_info", text="Input", icon='QUESTION') + props.filepath = cachelib.input_filepath + props.use_cache_info = True + props = row.operator("cachelibrary.archive_info", text="Output", icon='QUESTION') + props.filepath = cachelib.output_filepath + props.use_cache_info = True + + if info: + row = layout.row() + row.enabled = bool(info.filepath) + props = layout.operator("cachelibrary.archive_info", text="Calculate Size", icon='FILE_REFRESH') + props.filepath = info.filepath + props.use_cache_info = True + props.calc_bytes_size = True + + layout.separator() + + layout.prop(info, "filepath", text="File") + self.draw_info(context, layout, info) + + class OBJECT_PT_relations_extras(ObjectButtonsPanel, Panel): bl_label = "Relations Extras" bl_options = {'DEFAULT_CLOSED'} |