diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index cc132a2c3b2..82029e3a7af 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -25,6 +25,10 @@ elseif(CMAKE_COMPILER_IS_GNUCC) set(CYCLES_SSE2_KERNEL_FLAGS "-ffast-math -msse -msse2 -mfpmath=sse") set(CYCLES_SSE3_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -mfpmath=sse") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CYCLES_SSE2_KERNEL_FLAGS "-ffast-math -msse -msse2") + set(CYCLES_SSE3_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") endif() # for OSL diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index d628fa04f92..90278f215c0 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -462,6 +462,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri mesh->name = ustring(b_ob_data.name().c_str()); if(render_layer.use_surfaces || render_layer.use_hair) { + if(preview) + b_ob.update_from_editmode(); + BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed); if(b_mesh) { diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 572cfae45cd..b509134512b 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -223,7 +223,7 @@ bool RenderBuffers::get_pass_rect(PassType type, float exposure, int sample, int float3 f = make_float3(in[0], in[1], in[2]); float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); - f = safe_divide_color(f*exposure, f_divide); + f = safe_divide_even_color(f*exposure, f_divide); pixels[0] = f.x; pixels[1] = f.y; diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 14ebf311a4b..cde547cd77c 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -1100,6 +1100,42 @@ __device_inline float3 safe_divide_color(float3 a, float3 b) return make_float3(x, y, z); } +__device_inline float3 safe_divide_even_color(float3 a, float3 b) +{ + float x, y, z; + + x = (b.x != 0.0f)? a.x/b.x: 0.0f; + y = (b.y != 0.0f)? a.y/b.y: 0.0f; + z = (b.z != 0.0f)? a.z/b.z: 0.0f; + + /* try to get grey even if b is zero */ + if(b.x == 0.0f) { + if(b.y == 0.0f) { + x = z; + y = z; + } + else if(b.z == 0.0f) { + x = y; + z = y; + } + else + x = 0.5f*(y + z); + } + else if(b.y == 0.0f) { + if(b.z == 0.0f) { + y = x; + z = x; + } + else + y = 0.5f*(x + z); + } + else if(b.z == 0.0f) { + z = 0.5f*(x + y); + } + + return make_float3(x, y, z); +} + /* Rotation of point around axis and angle */ __device_inline float3 rotate_around_axis(float3 p, float3 axis, float angle) diff --git a/release/scripts/freestyle/style_modules/parameter_editor.py b/release/scripts/freestyle/style_modules/parameter_editor.py index 71cfdce43f0..5b773cd7a6d 100644 --- a/release/scripts/freestyle/style_modules/parameter_editor.py +++ b/release/scripts/freestyle/style_modules/parameter_editor.py @@ -32,7 +32,7 @@ from freestyle import BackboneStretcherShader, BezierCurveShader, BinaryPredicat FalseBP1D, FalseUP1D, GuidingLinesShader, Interface0DIterator, Nature, Noise, Normal2DF0D, Operators, \ PolygonalizationShader, QuantitativeInvisibilityF1D, QuantitativeInvisibilityUP1D, SamplingShader, \ SpatialNoiseShader, StrokeAttribute, StrokeShader, TipRemoverShader, TrueBP1D, TrueUP1D, UnaryPredicate0D, \ - UnaryPredicate1D, VertexOrientation2DF0D, WithinImageBoundaryUP1D + UnaryPredicate1D, VertexOrientation2DF0D, WithinImageBoundaryUP1D, ContextFunctions from Functions0D import CurveMaterialF0D from PredicatesU1D import pyNatureUP1D from logical_operators import AndUP1D, NotUP1D, OrUP1D @@ -1046,17 +1046,7 @@ def process(layer_name, lineset_name): selection_criteria.append(upred) # prepare selection criteria by image border if lineset.select_by_image_border: - fac = scene.render.resolution_percentage / 100.0 - w = scene.render.resolution_x * fac - h = scene.render.resolution_y * fac - if scene.render.use_border: - xmin = scene.render.border_min_x * w - xmax = scene.render.border_max_x * w - ymin = scene.render.border_min_y * h - ymax = scene.render.border_max_y * h - else: - xmin, xmax = 0.0, float(w) - ymin, ymax = 0.0, float(h) + xmin, ymin, xmax, ymax = ContextFunctions.get_border() upred = WithinImageBoundaryUP1D(xmin, ymin, xmax, ymax) selection_criteria.append(upred) # select feature edges diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py new file mode 100644 index 00000000000..727e9935bcc --- /dev/null +++ b/release/scripts/modules/rna_keymap_ui.py @@ -0,0 +1,384 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# 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. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +__all__ = ( + "draw_entry", + "draw_km", + "draw_kmi", + "draw_filtered", + "draw_hierarchy", + "draw_keymaps", + ) + + +import bpy +from bpy.app.translations import pgettext_iface as iface_ +from bpy.app.translations import contexts as i18n_contexts + + +def _indented_layout(layout, level): + indentpx = 16 + if level == 0: + level = 0.0001 # Tweak so that a percentage of 0 won't split by half + indent = level * indentpx / bpy.context.region.width + + split = layout.split(percentage=indent) + col = split.column() + col = split.column() + return col + + +def draw_entry(display_keymaps, entry, col, level=0): + idname, spaceid, regionid, children = entry + + for km, kc in display_keymaps: + if km.name == idname and km.space_type == spaceid and km.region_type == regionid: + draw_km(display_keymaps, kc, km, children, col, level) + + ''' + km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid) + if not km: + kc = defkc + km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid) + + if km: + draw_km(kc, km, children, col, level) + ''' + + +def draw_km(display_keymaps, kc, km, children, layout, level): + km = km.active() + + layout.context_pointer_set("keymap", km) + + col = _indented_layout(layout, level) + + row = col.row() + row.prop(km, "show_expanded_children", text="", emboss=False) + row.label(text=km.name, text_ctxt=i18n_contexts.id_windowmanager) + + row.label() + row.label() + + if km.is_modal: + row.label(text="", icon='LINKED') + if km.is_user_modified: + row.operator("wm.keymap_restore", text="Restore") + else: + row.label() + + if km.show_expanded_children: + if children: + # Put the Parent key map's entries in a 'global' sub-category + # equal in hierarchy to the other children categories + subcol = _indented_layout(col, level + 1) + subrow = subcol.row() + subrow.prop(km, "show_expanded_items", text="", emboss=False) + subrow.label(text=iface_("%s (Global)") % km.name, translate=False) + else: + km.show_expanded_items = True + + # Key Map items + if km.show_expanded_items: + for kmi in km.keymap_items: + draw_kmi(display_keymaps, kc, km, kmi, col, level + 1) + + # "Add New" at end of keymap item list + col = _indented_layout(col, level + 1) + subcol = col.split(percentage=0.2).column() + subcol.operator("wm.keyitem_add", text="Add New", text_ctxt=i18n_contexts.id_windowmanager, + icon='ZOOMIN') + + col.separator() + + # Child key maps + if children: + subcol = col.column() + row = subcol.row() + + for entry in children: + draw_entry(display_keymaps, entry, col, level + 1) + + +def draw_kmi(display_keymaps, kc, km, kmi, layout, level): + map_type = kmi.map_type + + col = _indented_layout(layout, level) + + if kmi.show_expanded: + col = col.column(align=True) + box = col.box() + else: + box = col.column() + + split = box.split(percentage=0.05) + + # header bar + row = split.row() + row.prop(kmi, "show_expanded", text="", emboss=False) + + row = split.row() + row.prop(kmi, "active", text="", emboss=False) + + if km.is_modal: + row.prop(kmi, "propvalue", text="") + else: + row.label(text=kmi.name) + + row = split.row() + row.prop(kmi, "map_type", text="") + if map_type == 'KEYBOARD': + row.prop(kmi, "type", text="", full_event=True) + elif map_type == 'MOUSE': + row.prop(kmi, "type", text="", full_event=True) + elif map_type == 'NDOF': + row.prop(kmi, "type", text="", full_event=True) + elif map_type == 'TWEAK': + subrow = row.row() + subrow.prop(kmi, "type", text="") + subrow.prop(kmi, "value", text="") + elif map_type == 'TIMER': + row.prop(kmi, "type", text="") + else: + row.label() + + if (not kmi.is_user_defined) and kmi.is_user_modified: + row.operator("wm.keyitem_restore", text="", icon='BACK').item_id = kmi.id + else: + row.operator("wm.keyitem_remove", text="", icon='X').item_id = kmi.id + + # Expanded, additional event settings + if kmi.show_expanded: + box = col.box() + + split = box.split(percentage=0.4) + sub = split.row() + + if km.is_modal: + sub.prop(kmi, "propvalue", text="") + else: + # One day... + #~ sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="") + sub.prop(kmi, "idname", text="") + + if map_type not in {'TEXTINPUT', 'TIMER'}: + sub = split.column() + subrow = sub.row(align=True) + + if map_type == 'KEYBOARD': + subrow.prop(kmi, "type", text="", event=True) + subrow.prop(kmi, "value", text="") + elif map_type in {'MOUSE', 'NDOF'}: + subrow.prop(kmi, "type", text="") + subrow.prop(kmi, "value", text="") + + subrow = sub.row() + subrow.scale_x = 0.75 + subrow.prop(kmi, "any") + subrow.prop(kmi, "shift") + subrow.prop(kmi, "ctrl") + subrow.prop(kmi, "alt") + subrow.prop(kmi, "oskey", text="Cmd") + subrow.prop(kmi, "key_modifier", text="", event=True) + + # Operator properties + box.template_keymap_item_properties(kmi) + + # Modal key maps attached to this operator + if not km.is_modal: + kmm = kc.keymaps.find_modal(kmi.idname) + if kmm: + draw_km(display_keymaps, kc, kmm, None, layout, level + 1) + layout.context_pointer_set("keymap", km) + +_EVENT_TYPES = set() +_EVENT_TYPE_MAP = {} + + +def draw_filtered(display_keymaps, filter_type, filter_text, layout): + + if filter_type == 'NAME': + def filter_func(kmi): + return (filter_text in kmi.idname.lower() or + filter_text in kmi.name.lower()) + else: + if not _EVENT_TYPES: + enum = bpy.types.Event.bl_rna.properties["type"].enum_items + _EVENT_TYPES.update(enum.keys()) + _EVENT_TYPE_MAP.update({item.name.replace(" ", "_").upper(): key + for key, item in enum.items()}) + + del enum + _EVENT_TYPE_MAP.update({ + "`": 'ACCENT_GRAVE', + "*": 'NUMPAD_ASTERIX', + "/": 'NUMPAD_SLASH', + "RMB": 'RIGHTMOUSE', + "LMB": 'LEFTMOUSE', + "MMB": 'MIDDLEMOUSE', + }) + _EVENT_TYPE_MAP.update({ + "%d" % i: "NUMPAD_%d" % i for i in range(9) + }) + # done with once off init + + filter_text_split = filter_text.strip() + filter_text_split = filter_text.split() + + # Modifier {kmi.attribute: name} mapping + key_mod = { + "ctrl": "ctrl", + "alt": "alt", + "shift": "shift", + "cmd": "oskey", + "oskey": "oskey", + "any": "any", + } + # KeyMapItem like dict, use for comparing against + # attr: state + kmi_test_dict = {} + + # initialize? - so if a if a kmi has a MOD assigned it wont show up. + #~ for kv in key_mod.values(): + #~ kmi_test_dict[kv] = False + + # altname: attr + for kk, kv in key_mod.items(): + if kk in filter_text_split: + filter_text_split.remove(kk) + kmi_test_dict[kv] = True + # whats left should be the event type + if len(filter_text_split) > 1: + return False + elif filter_text_split: + kmi_type = filter_text_split[0].upper() + + if kmi_type not in _EVENT_TYPES: + # replacement table + kmi_type_test = _EVENT_TYPE_MAP.get(kmi_type) + if kmi_type_test is None: + # print("Unknown Type:", kmi_type) + + # Partial match + for k, v in _EVENT_TYPE_MAP.items(): + if kmi_type in k: + kmi_type_test = v + break + if kmi_type in v: + kmi_type_test = v + break + + if kmi_type_test is None: + return False + + kmi_type = kmi_type_test + del kmi_type_test + + kmi_test_dict["type"] = kmi_type + + # main filter func, runs many times + def filter_func(kmi): + for kk, ki in kmi_test_dict.items(): + if getattr(kmi, kk) != ki: + return False + return True + + for km, kc in display_keymaps: + km = km.active() + layout.context_pointer_set("keymap", km) + + filtered_items = [kmi for kmi in km.keymap_items if filter_func(kmi)] + + if filtered_items: + col = layout.column() + + row = col.row() + row.label(text=km.name, icon='DOT') + + row.label() + row.label() + + if km.is_user_modified: + row.operator("wm.keymap_restore", text="Restore") + else: + row.label() + + for kmi in filtered_items: + draw_kmi(display_keymaps, kc, km, kmi, col, 1) + + # "Add New" at end of keymap item list + col = _indented_layout(layout, 1) + subcol = col.split(percentage=0.2).column() + subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') + return True + + +def draw_hierarchy(display_keymaps, layout): + from bpy_extras import keyconfig_utils + for entry in keyconfig_utils.KM_HIERARCHY: + draw_entry(display_keymaps, entry, layout) + + +def draw_keymaps(context, layout): + from bpy_extras import keyconfig_utils + + wm = context.window_manager + kc = wm.keyconfigs.user + spref = context.space_data + + col = layout.column() + sub = col.column() + + subsplit = sub.split() + subcol = subsplit.column() + + row = subcol.row(align=True) + + #~ row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:") + text = bpy.path.display_name(wm.keyconfigs.active.name) + if not text: + text = "Blender (default)" + row.menu("USERPREF_MT_keyconfigs", text=text) + row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMIN') + row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMOUT').remove_active = True + + #~ layout.context_pointer_set("keyconfig", wm.keyconfigs.active) + #~ row.operator("wm.keyconfig_remove", text="", icon='X') + row.separator() + rowsub = row.split(align=True, percentage=0.33) + # postpone drawing into rowsub, so we can set alert! + + col.separator() + display_keymaps = keyconfig_utils.keyconfig_merge(kc, kc) + filter_type = spref.filter_type + filter_text = spref.filter_text.strip() + if filter_text: + filter_text = filter_text.lower() + ok = draw_filtered(display_keymaps, filter_type, filter_text, col) + else: + draw_hierarchy(display_keymaps, col) + ok = True + + # go back and fill in rowsub + rowsub.prop(spref, "filter_type", text="") + rowsubsub = rowsub.row(align=True) + if not ok: + rowsubsub.alert = True + rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM') diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 594f724c6e3..b9f2e8406c6 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -72,7 +72,6 @@ _modules = [ "space_sequencer", "space_text", "space_time", - "space_userpref_keymap", "space_userpref", "space_view3d", "space_view3d_toolbar", diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 54387f10a8d..3e281e08983 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -917,9 +917,6 @@ class USERPREF_PT_file(Panel): col.prop(system, "author", text="") -from bl_ui.space_userpref_keymap import InputKeyMapPanel - - class USERPREF_MT_ndof_settings(Menu): # accessed from the window key-bindings in C (only) bl_label = "3D Mouse Settings" @@ -960,9 +957,25 @@ class USERPREF_MT_ndof_settings(Menu): layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM') -class USERPREF_PT_input(Panel, InputKeyMapPanel): +class USERPREF_MT_keyconfigs(Menu): + bl_label = "KeyPresets" + preset_subdir = "keyconfig" + preset_operator = "wm.keyconfig_activate" + + def draw(self, context): + props = self.layout.operator("wm.context_set_value", text="Blender (default)") + props.data_path = "window_manager.keyconfigs.active" + props.value = "context.window_manager.keyconfigs.default" + + # now draw the presets + Menu.draw_preset(self, context) + + +class USERPREF_PT_input(Panel): bl_space_type = 'USER_PREFERENCES' bl_label = "Input" + bl_region_type = 'WINDOW' + bl_options = {'HIDE_HEADER'} @classmethod def poll(cls, context): @@ -1039,6 +1052,8 @@ class USERPREF_PT_input(Panel, InputKeyMapPanel): row.separator() def draw(self, context): + from rna_keymap_ui import draw_keymaps + layout = self.layout #import time @@ -1055,7 +1070,7 @@ class USERPREF_PT_input(Panel, InputKeyMapPanel): self.draw_input_prefs(inputs, split) # Keymap Settings - self.draw_keymaps(context, split) + draw_keymaps(context, split) #print("runtime", time.time() - start) diff --git a/release/scripts/startup/bl_ui/space_userpref_keymap.py b/release/scripts/startup/bl_ui/space_userpref_keymap.py deleted file mode 100644 index 8d6eb2c623d..00000000000 --- a/release/scripts/startup/bl_ui/space_userpref_keymap.py +++ /dev/null @@ -1,391 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# 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. -# -# ##### END GPL LICENSE BLOCK ##### - -# -import bpy -from bpy.types import Menu -from bpy.app.translations import pgettext_iface as iface_ -from bpy.app.translations import contexts as i18n_contexts - - -class USERPREF_MT_keyconfigs(Menu): - bl_label = "KeyPresets" - preset_subdir = "keyconfig" - preset_operator = "wm.keyconfig_activate" - - def draw(self, context): - props = self.layout.operator("wm.context_set_value", text="Blender (default)") - props.data_path = "window_manager.keyconfigs.active" - props.value = "context.window_manager.keyconfigs.default" - - # now draw the presets - Menu.draw_preset(self, context) - - -class InputKeyMapPanel: - bl_space_type = 'USER_PREFERENCES' - bl_label = "Input" - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} - - def draw_entry(self, display_keymaps, entry, col, level=0): - idname, spaceid, regionid, children = entry - - for km, kc in display_keymaps: - if km.name == idname and km.space_type == spaceid and km.region_type == regionid: - self.draw_km(display_keymaps, kc, km, children, col, level) - - ''' - km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid) - if not km: - kc = defkc - km = kc.keymaps.find(idname, space_type=spaceid, region_type=regionid) - - if km: - self.draw_km(kc, km, children, col, level) - ''' - - def indented_layout(self, layout, level): - indentpx = 16 - if level == 0: - level = 0.0001 # Tweak so that a percentage of 0 won't split by half - indent = level * indentpx / bpy.context.region.width - - split = layout.split(percentage=indent) - col = split.column() - col = split.column() - return col - - def draw_km(self, display_keymaps, kc, km, children, layout, level): - km = km.active() - - layout.context_pointer_set("keymap", km) - - col = self.indented_layout(layout, level) - - row = col.row() - row.prop(km, "show_expanded_children", text="", emboss=False) - row.label(text=km.name, text_ctxt=i18n_contexts.id_windowmanager) - - row.label() - row.label() - - if km.is_modal: - row.label(text="", icon='LINKED') - if km.is_user_modified: - row.operator("wm.keymap_restore", text="Restore") - else: - row.label() - - if km.show_expanded_children: - if children: - # Put the Parent key map's entries in a 'global' sub-category - # equal in hierarchy to the other children categories - subcol = self.indented_layout(col, level + 1) - subrow = subcol.row() - subrow.prop(km, "show_expanded_items", text="", emboss=False) - subrow.label(text=iface_("%s (Global)") % km.name, translate=False) - else: - km.show_expanded_items = True - - # Key Map items - if km.show_expanded_items: - for kmi in km.keymap_items: - self.draw_kmi(display_keymaps, kc, km, kmi, col, level + 1) - - # "Add New" at end of keymap item list - col = self.indented_layout(col, level + 1) - subcol = col.split(percentage=0.2).column() - subcol.operator("wm.keyitem_add", text="Add New", text_ctxt=i18n_contexts.id_windowmanager, - icon='ZOOMIN') - - col.separator() - - # Child key maps - if children: - subcol = col.column() - row = subcol.row() - - for entry in children: - self.draw_entry(display_keymaps, entry, col, level + 1) - - def draw_kmi(self, display_keymaps, kc, km, kmi, layout, level): - map_type = kmi.map_type - - col = self.indented_layout(layout, level) - - if kmi.show_expanded: - col = col.column(align=True) - box = col.box() - else: - box = col.column() - - split = box.split(percentage=0.05) - - # header bar - row = split.row() - row.prop(kmi, "show_expanded", text="", emboss=False) - - row = split.row() - row.prop(kmi, "active", text="", emboss=False) - - if km.is_modal: - row.prop(kmi, "propvalue", text="") - else: - row.label(text=kmi.name) - - row = split.row() - row.prop(kmi, "map_type", text="") - if map_type == 'KEYBOARD': - row.prop(kmi, "type", text="", full_event=True) - elif map_type == 'MOUSE': - row.prop(kmi, "type", text="", full_event=True) - elif map_type == 'NDOF': - row.prop(kmi, "type", text="", full_event=True) - elif map_type == 'TWEAK': - subrow = row.row() - subrow.prop(kmi, "type", text="") - subrow.prop(kmi, "value", text="") - elif map_type == 'TIMER': - row.prop(kmi, "type", text="") - else: - row.label() - - if (not kmi.is_user_defined) and kmi.is_user_modified: - row.operator("wm.keyitem_restore", text="", icon='BACK').item_id = kmi.id - else: - row.operator("wm.keyitem_remove", text="", icon='X').item_id = kmi.id - - # Expanded, additional event settings - if kmi.show_expanded: - box = col.box() - - split = box.split(percentage=0.4) - sub = split.row() - - if km.is_modal: - sub.prop(kmi, "propvalue", text="") - else: - # One day... - #~ sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="") - sub.prop(kmi, "idname", text="") - - if map_type not in {'TEXTINPUT', 'TIMER'}: - sub = split.column() - subrow = sub.row(align=True) - - if map_type == 'KEYBOARD': - subrow.prop(kmi, "type", text="", event=True) - subrow.prop(kmi, "value", text="") - elif map_type in {'MOUSE', 'NDOF'}: - subrow.prop(kmi, "type", text="") - subrow.prop(kmi, "value", text="") - - subrow = sub.row() - subrow.scale_x = 0.75 - subrow.prop(kmi, "any") - subrow.prop(kmi, "shift") - subrow.prop(kmi, "ctrl") - subrow.prop(kmi, "alt") - subrow.prop(kmi, "oskey", text="Cmd") - subrow.prop(kmi, "key_modifier", text="", event=True) - - # Operator properties - box.template_keymap_item_properties(kmi) - - # Modal key maps attached to this operator - if not km.is_modal: - kmm = kc.keymaps.find_modal(kmi.idname) - if kmm: - self.draw_km(display_keymaps, kc, kmm, None, layout, level + 1) - layout.context_pointer_set("keymap", km) - - _EVENT_TYPES = set() - _EVENT_TYPE_MAP = {} - - def draw_filtered(self, display_keymaps, filter_type, filter_text, layout): - - if filter_type == 'NAME': - def filter_func(kmi): - return (filter_text in kmi.idname.lower() or - filter_text in kmi.name.lower()) - else: - if not self._EVENT_TYPES: - enum = bpy.types.Event.bl_rna.properties["type"].enum_items - self._EVENT_TYPES.update(enum.keys()) - self._EVENT_TYPE_MAP.update({item.name.replace(" ", "_").upper(): key for key, item in enum.items()}) - - del enum - self._EVENT_TYPE_MAP.update({ - "`": 'ACCENT_GRAVE', - "*": 'NUMPAD_ASTERIX', - "/": 'NUMPAD_SLASH', - "RMB": 'RIGHTMOUSE', - "LMB": 'LEFTMOUSE', - "MMB": 'MIDDLEMOUSE', - }) - self._EVENT_TYPE_MAP.update({ - "%d" % i: "NUMPAD_%d" % i for i in range(9) - }) - # done with once off init - - filter_text_split = filter_text.strip() - filter_text_split = filter_text.split() - - # Modifier {kmi.attribute: name} mapping - key_mod = { - "ctrl": "ctrl", - "alt": "alt", - "shift": "shift", - "cmd": "oskey", - "oskey": "oskey", - "any": "any", - } - # KeyMapItem like dict, use for comparing against - # attr: state - kmi_test_dict = {} - - # initialize? - so if a if a kmi has a MOD assigned it wont show up. - #~ for kv in key_mod.values(): - #~ kmi_test_dict[kv] = False - - # altname: attr - for kk, kv in key_mod.items(): - if kk in filter_text_split: - filter_text_split.remove(kk) - kmi_test_dict[kv] = True - # whats left should be the event type - if len(filter_text_split) > 1: - return False - elif filter_text_split: - kmi_type = filter_text_split[0].upper() - - if kmi_type not in self._EVENT_TYPES: - # replacement table - kmi_type_test = self._EVENT_TYPE_MAP.get(kmi_type) - if kmi_type_test is None: - # print("Unknown Type:", kmi_type) - - # Partial match - for k, v in self._EVENT_TYPE_MAP.items(): - if kmi_type in k: - kmi_type_test = v - break - if kmi_type in v: - kmi_type_test = v - break - - if kmi_type_test is None: - return False - - kmi_type = kmi_type_test - del kmi_type_test - - kmi_test_dict["type"] = kmi_type - - # main filter func, runs many times - def filter_func(kmi): - for kk, ki in kmi_test_dict.items(): - if getattr(kmi, kk) != ki: - return False - return True - - for km, kc in display_keymaps: - km = km.active() - layout.context_pointer_set("keymap", km) - - filtered_items = [kmi for kmi in km.keymap_items if filter_func(kmi)] - - if filtered_items: - col = layout.column() - - row = col.row() - row.label(text=km.name, icon='DOT') - - row.label() - row.label() - - if km.is_user_modified: - row.operator("wm.keymap_restore", text="Restore") - else: - row.label() - - for kmi in filtered_items: - self.draw_kmi(display_keymaps, kc, km, kmi, col, 1) - - # "Add New" at end of keymap item list - col = self.indented_layout(layout, 1) - subcol = col.split(percentage=0.2).column() - subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN') - return True - - def draw_hierarchy(self, display_keymaps, layout): - from bpy_extras import keyconfig_utils - for entry in keyconfig_utils.KM_HIERARCHY: - self.draw_entry(display_keymaps, entry, layout) - - def draw_keymaps(self, context, layout): - from bpy_extras import keyconfig_utils - - wm = context.window_manager - kc = wm.keyconfigs.user - spref = context.space_data - - col = layout.column() - sub = col.column() - - subsplit = sub.split() - subcol = subsplit.column() - - row = subcol.row(align=True) - - #~ row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:") - text = bpy.path.display_name(wm.keyconfigs.active.name) - if not text: - text = "Blender (default)" - row.menu("USERPREF_MT_keyconfigs", text=text) - row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMIN') - row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMOUT').remove_active = True - - #~ layout.context_pointer_set("keyconfig", wm.keyconfigs.active) - #~ row.operator("wm.keyconfig_remove", text="", icon='X') - row.separator() - rowsub = row.split(align=True, percentage=0.33) - # postpone drawing into rowsub, so we can set alert! - - col.separator() - display_keymaps = keyconfig_utils.keyconfig_merge(kc, kc) - filter_type = spref.filter_type - filter_text = spref.filter_text.strip() - if filter_text: - filter_text = filter_text.lower() - ok = self.draw_filtered(display_keymaps, filter_type, filter_text, col) - else: - self.draw_hierarchy(display_keymaps, col) - ok = True - - # go back and fill in rowsub - rowsub.prop(spref, "filter_type", text="") - rowsubsub = rowsub.row(align=True) - if not ok: - rowsubsub.alert = True - rowsubsub.prop(spref, "filter_text", text="", icon='VIEWZOOM') - - -if __name__ == "__main__": # only for live edit. - bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 20f998cd568..7fe8d334572 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -617,8 +617,8 @@ class VIEW3D_MT_select_edit_mesh(Menu): layout.separator() - layout.operator("mesh.select_less", text="Less") layout.operator("mesh.select_more", text="More") + layout.operator("mesh.select_less", text="Less") layout.separator() diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 331cac3ed76..154986936a2 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -131,6 +131,18 @@ void BLI_rw_mutex_free(ThreadRWMutex *mutex); void BLI_rw_mutex_lock(ThreadRWMutex *mutex, int mode); void BLI_rw_mutex_unlock(ThreadRWMutex *mutex); +/* Ticket Mutex Lock + * + * This is a 'fair' mutex in that it will grant the lock to the first thread + * that requests it. */ + +typedef struct TicketMutex TicketMutex; + +TicketMutex *BLI_ticket_mutex_alloc(void); +void BLI_ticket_mutex_free(TicketMutex *ticket); +void BLI_ticket_mutex_lock(TicketMutex *ticket); +void BLI_ticket_mutex_unlock(TicketMutex *ticket); + /* ThreadedWorker * * A simple tool for dispatching work to a limited number of threads diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index e0ea3bbf685..2b6fb52c49c 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -508,6 +508,52 @@ void BLI_rw_mutex_free(ThreadRWMutex *mutex) MEM_freeN(mutex); } +/* Ticket Mutex Lock */ + +struct TicketMutex { + pthread_cond_t cond; + pthread_mutex_t mutex; + unsigned int queue_head, queue_tail; +}; + +TicketMutex *BLI_ticket_mutex_alloc(void) +{ + TicketMutex *ticket = MEM_callocN(sizeof(TicketMutex), "TicketMutex"); + + pthread_cond_init(&ticket->cond, NULL); + pthread_mutex_init(&ticket->mutex, NULL); + + return ticket; +} + +void BLI_ticket_mutex_free(TicketMutex *ticket) +{ + pthread_mutex_destroy(&ticket->mutex); + pthread_cond_destroy(&ticket->cond); + MEM_freeN(ticket); +} + +void BLI_ticket_mutex_lock(TicketMutex *ticket) +{ + unsigned int queue_me; + + pthread_mutex_lock(&ticket->mutex); + queue_me = ticket->queue_tail++; + + while (queue_me != ticket->queue_head) + pthread_cond_wait(&ticket->cond, &ticket->mutex); + + pthread_mutex_unlock(&ticket->mutex); +} + +void BLI_ticket_mutex_unlock(TicketMutex *ticket) +{ + pthread_mutex_lock(&ticket->mutex); + ticket->queue_head++; + pthread_cond_broadcast(&ticket->cond); + pthread_mutex_unlock(&ticket->mutex); +} + /* ************************************************ */ typedef struct ThreadedWorker { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 5d30bf6ff73..a6da0762b1c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5708,8 +5708,9 @@ static void lib_link_screen(FileData *fd, Main *main) ntree = nodetree_from_id(snode->id); if (ntree) snode->nodetree = ntree; - else - snode->nodetree = newlibadr(fd, sc->id.lib, snode->nodetree); + else { + snode->nodetree = newlibadr_us(fd, sc->id.lib, snode->nodetree); + } for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { @@ -5717,7 +5718,7 @@ static void lib_link_screen(FileData *fd, Main *main) path->nodetree = snode->nodetree; } else - path->nodetree = newlibadr(fd, sc->id.lib, path->nodetree); + path->nodetree = newlibadr_us(fd, sc->id.lib, path->nodetree); if (!path->nodetree) break; @@ -6046,7 +6047,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc path->nodetree = snode->nodetree; } else - path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, 0); + path->nodetree= restore_pointer_by_name(newmain, (ID*)path->nodetree, 2); if (!path->nodetree) break; diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index e6b5fc15f52..b3dd2f57523 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC operators/bmo_join_triangles.c operators/bmo_mesh_conv.c operators/bmo_mirror.c + operators/bmo_normals.c operators/bmo_poke.c operators/bmo_primitive.c operators/bmo_removedoubles.c diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 7e4d5e06100..497c9e1c153 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -150,7 +150,6 @@ static BMOpDefine bmo_recalc_face_normals_def = { "recalc_face_normals", /* slots_in */ {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, - {"use_face_tag", BMO_OP_SLOT_BOOL}, /* Tag faces that have been flipped */ {{'\0'}}, }, {{{'\0'}}}, /* no output */ diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index be186e0441b..a00bef0228b 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -1758,6 +1758,119 @@ float BM_mesh_calc_volume(BMesh *bm, bool is_signed) return vol; } + +/** + * TODO (as we need) + * - option to walk over faces by verts. + * - option to walk over non manifold edges. + * + * \param bm the BMesh. + * \param r_groups_array Array of ints to fill in, length of bm->totface. + * \param r_group_index index, length pairs into \a r_groups_array, size of return value + * int pairs: (array_start, array_length). + * \return The number of groups found. + */ +int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + void *user_data, bool (*filter_fn)(BMEdge *, void *user_data)) +{ +#ifdef DEBUG + int group_index_len = 1; +#else + int group_index_len = 32; +#endif + + int (*group_index)[2] = MEM_mallocN(sizeof(*group_index) * group_index_len, __func__); + + int *group_array = r_groups_array; + STACK_DECLARE(group_array); + + int group_curr = 0; + + const unsigned int tot_faces = bm->totface; + unsigned int tot_touch = 0; + + BMFace **fstack; + STACK_DECLARE(fstack); + + BMIter iter; + BMFace *f; + int i; + + STACK_INIT(group_array); + + /* init the array */ + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + BM_elem_index_set(f, i); /* set_inline */ + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + bm->elem_index_dirty &= ~BM_FACE; + + /* detect groups */ + fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); + + while (tot_touch != tot_faces) { + int *fg; + bool ok = false; + + BLI_assert(tot_touch < tot_faces); + + STACK_INIT(fstack); + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + STACK_PUSH(fstack, f); + ok = true; + break; + } + } + + BLI_assert(ok == true); + + /* manage arrays */ + if (group_index_len == group_curr) { + group_index_len *= 2; + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_index_len); + } + + fg = group_index[group_curr]; + fg[0] = STACK_SIZE(group_array); + fg[1] = 0; + + while ((f = STACK_POP(fstack))) { + BMLoop *l_iter, *l_first; + + /* add face */ + STACK_PUSH(group_array, BM_elem_index_get(f)); + tot_touch++; + fg[1]++; + /* done */ + + /* search for other faces */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMLoop *l_other = l_iter->radial_next; + if ((l_other != l_iter) && filter_fn(l_iter->e, user_data)) { + if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(l_other->f, BM_ELEM_TAG); + STACK_PUSH(fstack, l_other->f); + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + group_curr++; + } + + MEM_freeN(fstack); + + /* reduce alloc to required size */ + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + *r_group_index = group_index; + + return group_curr; +} + float bmesh_subd_falloff_calc(const int falloff, float val) { switch (falloff) { diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index 7d599a9c8af..94dae1d1d23 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -115,6 +115,8 @@ bool BM_face_is_any_vert_flag_test(BMFace *f, const char hflag); bool BM_face_is_any_edge_flag_test(BMFace *f, const char hflag); float BM_mesh_calc_volume(BMesh *bm, bool is_signed); +int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int (**r_group_index)[2], + void *user_data, bool (*filter_fn)(BMEdge *, void *user_data)); /* not really any good place to put this */ float bmesh_subd_falloff_calc(const int falloff, float val); diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c new file mode 100644 index 00000000000..c4200a37748 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -0,0 +1,186 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * Contributor(s): Joseph Eagar, Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_normals.c + * \ingroup bmesh + * + * normal recalculation. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +/********* righthand faces implementation ****** */ + +#define FACE_FLAG (1 << 0) +#define FACE_FLIP (1 << 1) +#define FACE_TEMP (1 << 2) + +static bool bmo_recalc_normal_edge_filter_cb(BMEdge *e, void *UNUSED(user_data)) +{ + return BM_edge_is_manifold(e); +} + +/** + * Given an array of faces, recalcualte their normals. + * this functions assumes all faces in the array are connected by edges. + * + * \param bm + * \param faces Array of connected faces. + * \param faces_len Length of \a faces + * \param oflag Flag to check before doing the actual face flipping. + */ +static void bmo_recalc_face_normals_array(BMesh *bm, BMFace **faces, const int faces_len, const short oflag) +{ + float cent[3]; + float (*faces_center)[3] = MEM_mallocN(sizeof(*faces_center) * faces_len, __func__); + const float cent_fac = 1.0f / (float)faces_len; + int i, f_start_index; + const short oflag_flip = oflag | FACE_FLIP; + + float f_len_best; + BMFace *f; + + BMFace **fstack = MEM_mallocN(sizeof(*fstack) * faces_len, __func__); + STACK_DECLARE(fstack); + + zero_v3(cent); + + /* first calculate the center */ + for (i = 0; i < faces_len; i++) { + float *f_cent = faces_center[i]; + BM_face_calc_center_mean_weighted(faces[i], f_cent); + madd_v3_v3fl(cent, f_cent, cent_fac); + + BLI_assert(BMO_elem_flag_test(bm, faces[i], FACE_TEMP) == 0); + } + + f_len_best = -FLT_MAX; + + for (i = 0; i < faces_len; i++) { + float f_len_test; + + if ((f_len_test = len_squared_v3v3(faces_center[i], cent)) > f_len_best) { + f_len_best = f_len_test; + f_start_index = i; + } + } + + /* make sure the starting face has the correct winding */ + if (dot_v3v3(faces_center[f_start_index], faces[f_start_index]->no) < 0.0f) { + BMO_elem_flag_enable(bm, faces[f_start_index], FACE_FLIP); + } + + MEM_freeN(faces_center); + + /* now that we've found our starting face, make all connected faces + * have the same winding. this is done recursively, using a manual + * stack (if we use simple function recursion, we'd end up overloading + * the stack on large meshes). */ + STACK_INIT(fstack); + + STACK_PUSH(fstack, faces[f_start_index]); + BMO_elem_flag_enable(bm, faces[f_start_index], FACE_TEMP); + + while ((f = STACK_POP(fstack))) { + const bool flip_state = BMO_elem_flag_test_bool(bm, f, FACE_FLIP); + BMLoop *l_iter, *l_first; + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMLoop *l_other = l_iter->radial_next; + + if ((l_other != l_iter) && bmo_recalc_normal_edge_filter_cb(l_iter->e, NULL)) { + if (!BMO_elem_flag_test(bm, l_other->f, FACE_TEMP)) { + BMO_elem_flag_enable(bm, l_other->f, FACE_TEMP); + BMO_elem_flag_set(bm, l_other->f, FACE_FLIP, (l_other->v == l_iter->v) != flip_state); + STACK_PUSH(fstack, l_other->f); + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + MEM_freeN(fstack); + + /* apply flipping to oflag'd faces */ + for (i = 0; i < faces_len; i++) { + if (BMO_elem_flag_test(bm, faces[i], oflag_flip) == oflag_flip) { + BM_face_normal_flip(bm, faces[i]); + } + BMO_elem_flag_disable(bm, faces[i], FACE_TEMP); + } +} + +/* + * put normal to the outside, and set the first direction flags in edges + * + * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces + * this is in fact the 'select connected' + * + * in case all faces were not done: start over with 'find the ultimate ...' */ + +void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) +{ + int *groups_array = MEM_mallocN(sizeof(groups_array) * bm->totface, __func__); + int faces_len; + BMFace **faces_arr = BM_iter_as_arrayN(bm, BM_FACES_OF_MESH, NULL, &faces_len, NULL, 0); + BMFace **faces_grp = MEM_mallocN(sizeof(faces_grp) * bm->totface, __func__); + + int (*group_index)[2]; + const int group_tot = BM_mesh_calc_face_groups(bm, groups_array, &group_index, + NULL, bmo_recalc_normal_edge_filter_cb); + int i; + + + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_FLAG); + + for (i = 0; i < group_tot; i++) { + const int fg_sta = group_index[i][0]; + const int fg_len = group_index[i][1]; + int j; + bool is_calc = false; + + for (j = 0; j < fg_len; j++) { + faces_grp[j] = faces_arr[groups_array[fg_sta + j]]; + + if (is_calc == false) { + is_calc = BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); + } + } + + if (is_calc) { + bmo_recalc_face_normals_array(bm, faces_grp, fg_len, FACE_FLAG); + } + } + + + if (faces_arr) MEM_freeN(faces_arr); + MEM_freeN(faces_grp); + + MEM_freeN(groups_array); + MEM_freeN(group_index); +} diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index 5e61b8ea7ea..2a0a7864499 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -278,131 +278,6 @@ void bmo_region_extend_exec(BMesh *bm, BMOperator *op) BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out", BM_ALL_NOLOOP, SEL_FLAG); } -/********* righthand faces implementation ****** */ - -#define FACE_VIS 1 -#define FACE_FLAG 2 -// #define FACE_MARK 4 /* UNUSED */ -#define FACE_FLIP 8 - -/* NOTE: these are the original recalc_face_normals comment in editmesh_mods.c, - * copied here for reference. */ - -/* based at a select-connected to witness loose objects */ - -/* count per edge the amount of faces - * find the ultimate left, front, upper face (not manhattan dist!!) - * also evaluate both triangle cases in quad, since these can be non-flat - * - * put normal to the outside, and set the first direction flags in edges - * - * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces - * this is in fact the 'select connected' - * - * in case (selected) faces were not done: start over with 'find the ultimate ...' */ - -/* NOTE: this function uses recursion, which is a little unusual for a bmop - * function, but acceptable I think. */ - -/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ - -void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) -{ - BMIter liter, liter2; - BMOIter siter; - BMFace *f, *startf; - BMFace **fstack; - STACK_DECLARE(fstack); - BMLoop *l, *l2; - float maxx, maxx_test, cent[3]; - const bool use_flip = BMO_slot_bool_get(op->slots_in, "use_face_tag"); - - startf = NULL; - maxx = -1.0e10; - - BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_FLAG); - - /* find a starting face */ - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - - /* clear dirty flag */ - BM_elem_flag_disable(f, BM_ELEM_TAG); - - if (BMO_elem_flag_test(bm, f, FACE_VIS)) - continue; - - if (!startf) startf = f; - - BM_face_calc_center_bounds(f, cent); - - if ((maxx_test = dot_v3v3(cent, cent)) > maxx) { - maxx = maxx_test; - startf = f; - } - } - - if (!startf) return; - - BM_face_calc_center_bounds(startf, cent); - - /* make sure the starting face has the correct winding */ - if (dot_v3v3(cent, startf->no) < 0.0f) { - BM_face_normal_flip(bm, startf); - BMO_elem_flag_toggle(bm, startf, FACE_FLIP); - - if (use_flip) - BM_elem_flag_toggle(startf, BM_ELEM_TAG); - } - - /* now that we've found our starting face, make all connected faces - * have the same winding. this is done recursively, using a manual - * stack (if we use simple function recursion, we'd end up overloading - * the stack on large meshes). */ - fstack = MEM_mallocN(sizeof(*fstack) * BMO_slot_buffer_count(op->slots_in, "faces"), __func__); - STACK_INIT(fstack); - STACK_PUSH(fstack, startf); - BMO_elem_flag_enable(bm, startf, FACE_VIS); - - while ((f = STACK_POP(fstack))) { - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_ITER_ELEM (l2, &liter2, l, BM_LOOPS_OF_LOOP) { - if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l) - continue; - - if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) { - BMO_elem_flag_enable(bm, l2->f, FACE_VIS); - - if (l2->v == l->v) { - BM_face_normal_flip(bm, l2->f); - - BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP); - if (use_flip) - BM_elem_flag_toggle(l2->f, BM_ELEM_TAG); - } - else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { - if (use_flip) { - BM_elem_flag_disable(l->f, BM_ELEM_TAG); - BM_elem_flag_disable(l2->f, BM_ELEM_TAG); - } - } - - STACK_PUSH(fstack, l2->f); - } - } - } - } - - MEM_freeN(fstack); - - /* check if we have faces yet to do. if so, recurse */ - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - if (!BMO_elem_flag_test(bm, f, FACE_VIS)) { - bmo_recalc_face_normals_exec(bm, op); - break; - } - } -} - void bmo_smooth_vert_exec(BMesh *UNUSED(bm), BMOperator *op) { BMOIter siter; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index d7073ef61be..e0c11414e38 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -2223,6 +2223,7 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) BMVert *bmv1, *bmv2, *bmv3, *bmv4, *bmv1i, *bmv2i, *bmv3i, *bmv4i; VMesh *vm1, *vm2; EdgeHalf *e1, *e2; + BMEdge *bme1, *bme2; BMFace *f1, *f2, *f; int k, nseg, i1, i2, odd, mid; @@ -2294,6 +2295,13 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme) bev_merge_end_uvs(bm, bv1, e1); if (!e2->is_seam && bv2->vmesh->mesh_kind == M_NONE) bev_merge_end_uvs(bm, bv2, e2); + + /* Copy edge data to first and last edge */ + bme1 = BM_edge_exists(bmv1, bmv2); + bme2 = BM_edge_exists(bmv3, bmv4); + BLI_assert(bme1 && bme2); + BM_elem_attrs_copy(bm, bm, bme, bme1); + BM_elem_attrs_copy(bm, bm, bme, bme2); } /* diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 3327d4a07e9..0f96e886ef3 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -2833,6 +2833,44 @@ void CURVE_OT_reveal(wmOperatorType *ot) /********************** subdivide operator *********************/ +static BezTriple *next_spline_bezier_point_get(Nurb *nu, BezTriple *bezt) +{ + BezTriple *nextbezt; + + if (bezt == nu->bezt + nu->pntsu - 1) { + if (nu->flagu & CU_NURB_CYCLIC) { + nextbezt = nu->bezt; + } + else { + nextbezt = NULL; + } + } + else { + nextbezt = bezt + 1; + } + + return nextbezt; +} + +static BPoint *next_spline_bpoint_get(Nurb *nu, BPoint *bp) +{ + BPoint *nextbp; + + if (bp == nu->bp + nu->pntsu - 1) { + if (nu->flagu & CU_NURB_CYCLIC) { + nextbp = nu->bp; + } + else { + nextbp = NULL; + } + } + else { + nextbp = bp + 1; + } + + return nextbp; +} + /** Divide the line segments associated with the currently selected * curve nodes (Bezier or NURB). If there are no valid segment * selections within the current selection, nothing happens. @@ -2842,7 +2880,7 @@ static void subdividenurb(Object *obedit, int number_cuts) Curve *cu = obedit->data; EditNurb *editnurb = cu->editnurb; Nurb *nu; - BezTriple *prevbezt, *bezt, *beztnew, *beztn; + BezTriple *bezt, *beztnew, *beztn; BPoint *bp, *prevbp, *bpnew, *bpn; float vec[15]; int a, b, sel, amount, *usel, *vsel, i; @@ -2853,25 +2891,25 @@ static void subdividenurb(Object *obedit, int number_cuts) for (nu = editnurb->nurbs.first; nu; nu = nu->next) { amount = 0; if (nu->type == CU_BEZIER) { + BezTriple *nextbezt; + /* * Insert a point into a 2D Bezier curve. * Endpoints are preserved. Otherwise, all selected and inserted points are * newly created. Old points are discarded. */ /* count */ - if (nu->flagu & CU_NURB_CYCLIC) { - a = nu->pntsu; - bezt = nu->bezt; - prevbezt = bezt + (a - 1); - } - else { - a = nu->pntsu - 1; - prevbezt = nu->bezt; - bezt = prevbezt + 1; - } + a = nu->pntsu; + bezt = nu->bezt; while (a--) { - if (BEZSELECTED_HIDDENHANDLES(cu, prevbezt) && BEZSELECTED_HIDDENHANDLES(cu, bezt) ) amount += number_cuts; - prevbezt = bezt; + nextbezt = next_spline_bezier_point_get(nu, bezt); + if (nextbezt == NULL) { + break; + } + + if (BEZSELECTED_HIDDENHANDLES(cu, bezt) && BEZSELECTED_HIDDENHANDLES(cu, nextbezt)) { + amount += number_cuts; + } bezt++; } @@ -2879,35 +2917,32 @@ static void subdividenurb(Object *obedit, int number_cuts) /* insert */ beztnew = (BezTriple *)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb"); beztn = beztnew; - if (nu->flagu & CU_NURB_CYCLIC) { - a = nu->pntsu; - bezt = nu->bezt; - prevbezt = bezt + (a - 1); - } - else { - a = nu->pntsu - 1; - prevbezt = nu->bezt; - bezt = prevbezt + 1; - } + a = nu->pntsu; + bezt = nu->bezt; while (a--) { - memcpy(beztn, prevbezt, sizeof(BezTriple)); - keyIndex_updateBezt(editnurb, prevbezt, beztn, 1); + memcpy(beztn, bezt, sizeof(BezTriple)); + keyIndex_updateBezt(editnurb, bezt, beztn, 1); beztn++; - if (BEZSELECTED_HIDDENHANDLES(cu, prevbezt) && BEZSELECTED_HIDDENHANDLES(cu, bezt)) { + nextbezt = next_spline_bezier_point_get(nu, bezt); + if (nextbezt == NULL) { + break; + } + + if (BEZSELECTED_HIDDENHANDLES(cu, bezt) && BEZSELECTED_HIDDENHANDLES(cu, nextbezt)) { float prevvec[3][3]; - memcpy(prevvec, prevbezt->vec, sizeof(float) * 9); + memcpy(prevvec, bezt->vec, sizeof(float) * 9); for (i = 0; i < number_cuts; i++) { factor = 1.0f / (number_cuts + 1 - i); - memcpy(beztn, bezt, sizeof(BezTriple)); + memcpy(beztn, nextbezt, sizeof(BezTriple)); /* midpoint subdividing */ interp_v3_v3v3(vec, prevvec[1], prevvec[2], factor); - interp_v3_v3v3(vec + 3, prevvec[2], bezt->vec[0], factor); - interp_v3_v3v3(vec + 6, bezt->vec[0], bezt->vec[1], factor); + interp_v3_v3v3(vec + 3, prevvec[2], nextbezt->vec[0], factor); + interp_v3_v3v3(vec + 6, nextbezt->vec[0], nextbezt->vec[1], factor); interp_v3_v3v3(vec + 9, vec, vec + 3, factor); interp_v3_v3v3(vec + 12, vec + 3, vec + 6, factor); @@ -2920,24 +2955,18 @@ static void subdividenurb(Object *obedit, int number_cuts) copy_v3_v3(beztn->vec[2], vec + 12); /* handle of next bezt */ if (a == 0 && i == number_cuts - 1 && (nu->flagu & CU_NURB_CYCLIC)) { copy_v3_v3(beztnew->vec[0], vec + 6); } - else { copy_v3_v3(bezt->vec[0], vec + 6); } + else { copy_v3_v3(nextbezt->vec[0], vec + 6); } - beztn->radius = (prevbezt->radius + bezt->radius) / 2; - beztn->weight = (prevbezt->weight + bezt->weight) / 2; + beztn->radius = (bezt->radius + nextbezt->radius) / 2; + beztn->weight = (bezt->weight + nextbezt->weight) / 2; memcpy(prevvec, beztn->vec, sizeof(float) * 9); beztn++; } } - prevbezt = bezt; bezt++; } - /* last point */ - if ((nu->flagu & CU_NURB_CYCLIC) == 0) { - memcpy(beztn, prevbezt, sizeof(BezTriple)); - keyIndex_updateBezt(editnurb, prevbezt, beztn, 1); - } MEM_freeN(nu->bezt); nu->bezt = beztnew; @@ -2947,6 +2976,8 @@ static void subdividenurb(Object *obedit, int number_cuts) } } /* End of 'if (nu->type == CU_BEZIER)' */ else if (nu->pntsv == 1) { + BPoint *nextbp; + /* * All flat lines (ie. co-planar), except flat Nurbs. Flat NURB curves * are handled together with the regular NURB plane division, as it @@ -2954,19 +2985,17 @@ static void subdividenurb(Object *obedit, int number_cuts) * stable... nzc 30-5-'00 */ /* count */ - if (nu->flagu & CU_NURB_CYCLIC) { - a = nu->pntsu; - bp = nu->bp; - prevbp = bp + (a - 1); - } - else { - a = nu->pntsu - 1; - prevbp = nu->bp; - bp = prevbp + 1; - } + a = nu->pntsu; + bp = nu->bp; while (a--) { - if ( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) amount += number_cuts; - prevbp = bp; + nextbp = next_spline_bpoint_get(nu, bp); + if (nextbp == NULL) { + break; + } + + if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) { + amount += number_cuts; + } bp++; } @@ -2975,39 +3004,33 @@ static void subdividenurb(Object *obedit, int number_cuts) bpnew = (BPoint *)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2"); bpn = bpnew; - if (nu->flagu & CU_NURB_CYCLIC) { - a = nu->pntsu; - bp = nu->bp; - prevbp = bp + (a - 1); - } - else { - a = nu->pntsu - 1; - prevbp = nu->bp; - bp = prevbp + 1; - } + a = nu->pntsu; + bp = nu->bp; + while (a--) { - memcpy(bpn, prevbp, sizeof(BPoint)); - keyIndex_updateBP(editnurb, prevbp, bpn, 1); + /* Copy "old" point. */ + memcpy(bpn, bp, sizeof(BPoint)); + keyIndex_updateBP(editnurb, bp, bpn, 1); bpn++; - if ((bp->f1 & SELECT) && (prevbp->f1 & SELECT)) { + nextbp = next_spline_bpoint_get(nu, bp); + if (nextbp == NULL) { + break; + } + + if ((bp->f1 & SELECT) && (nextbp->f1 & SELECT)) { // printf("*** subdivideNurb: insert 'linear' point\n"); for (i = 0; i < number_cuts; i++) { factor = (float)(i + 1) / (number_cuts + 1); - memcpy(bpn, bp, sizeof(BPoint)); - interp_v4_v4v4(bpn->vec, prevbp->vec, bp->vec, factor); + memcpy(bpn, nextbp, sizeof(BPoint)); + interp_v4_v4v4(bpn->vec, bp->vec, nextbp->vec, factor); bpn++; } } - prevbp = bp; bp++; } - if ((nu->flagu & CU_NURB_CYCLIC) == 0) { /* last point */ - memcpy(bpn, prevbp, sizeof(BPoint)); - keyIndex_updateBP(editnurb, prevbp, bpn, 1); - } MEM_freeN(nu->bp); nu->bp = bpnew; diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index c1ddd9a6294..e8e7643164f 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -52,6 +52,8 @@ void ED_render_engine_changed(struct Main *bmain); void ED_render_engine_area_exit(struct ScrArea *sa); void ED_render_scene_update(struct Main *bmain, struct Scene *scene, int updated); +void ED_viewport_render_kill_jobs(const struct bContext *C); + /* render_preview.c */ /* stores rendered preview - is also used for icons */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 57392c60f80..e061ff35025 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3018,16 +3018,16 @@ static void template_keymap_item_properties(uiLayout *layout, const char *title, /* recurse for nested properties */ if (RNA_property_type(prop) == PROP_POINTER) { PointerRNA propptr = RNA_property_pointer_get(ptr, prop); - const char *name = RNA_property_ui_name(prop); if (propptr.data && RNA_struct_is_a(propptr.type, &RNA_OperatorProperties)) { + const char *name = RNA_property_ui_name(prop); template_keymap_item_properties(layout, name, &propptr); continue; } } /* add property */ - uiItemR(flow, ptr, RNA_property_identifier(prop), 0, NULL, ICON_NONE); + uiItemFullR(flow, ptr, prop, -1, 0, 0, NULL, ICON_NONE); } RNA_STRUCT_END; } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 2fa6812d131..2c721a0a9dc 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -1066,7 +1066,7 @@ static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op) /* doflip has to do with bmesh_rationalize_normals, it's an internal * thing */ - if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf use_face_tag=%b", BM_ELEM_SELECT, true)) + if (!EDBM_op_callf(em, op, "recalc_face_normals faces=%hf", BM_ELEM_SELECT)) return OPERATOR_CANCELLED; if (RNA_boolean_get(op->ptr, "inside")) diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index edd61bfc5c3..eb09606e57e 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -83,7 +83,7 @@ void TEXTURE_OT_envmap_clear_all(struct wmOperatorType *ot); /* render_internal.c */ void RENDER_OT_render(struct wmOperatorType *ot); -void render_view3d(struct RenderEngine *engine, const struct bContext *C); +void render_view3d_update(struct RenderEngine *engine, const struct bContext *C); void render_view3d_draw(struct RenderEngine *engine, const struct bContext *C); /* render_opengl.c uses this */ diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 09138a5523a..7a2ece66ba4 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -728,11 +728,13 @@ void RENDER_OT_render(wmOperatorType *ot) #define PR_UPDATE_VIEW 1 #define PR_UPDATE_RENDERSIZE 2 #define PR_UPDATE_MATERIAL 4 +#define PR_UPDATE_DATABASE 8 typedef struct RenderPreview { /* from wmJob */ void *owner; short *stop, *do_update; + wmJob *job; Scene *scene; ScrArea *sa; @@ -743,8 +745,6 @@ typedef struct RenderPreview { RenderEngine *engine; float viewmat[4][4]; - - int keep_data; } RenderPreview; static int render_view3d_disprect(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, rcti *disprect) @@ -845,7 +845,13 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda float clipsta, clipend, pixsize; bool orth, restore = 0; char name[32]; - + int update_flag; + + update_flag = rp->engine->job_update_flag; + rp->engine->job_update_flag = 0; + + //printf("ma %d res %d view %d db %d\n", update_flag & PR_UPDATE_MATERIAL, update_flag & PR_UPDATE_RENDERSIZE, update_flag & PR_UPDATE_VIEW, update_flag & PR_UPDATE_DATABASE); + G.is_break = FALSE; if (false == render_view3d_get_rects(rp->ar, rp->v3d, rp->rv3d, &viewplane, rp->engine, &clipsta, &clipend, &pixsize, &orth)) @@ -860,13 +866,6 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda sprintf(name, "View3dPreview %p", (void *)rp->ar); re = rp->engine->re = RE_GetRender(name); - if (rp->engine->re == NULL) { - - re = rp->engine->re = RE_NewRender(name); - - rp->keep_data = 0; - } - /* set this always, rp is different for each job */ RE_test_break_cb(re, rp, render_view3d_break); RE_display_draw_cb(re, rp, render_view3d_draw_update); @@ -874,7 +873,7 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda rstats = RE_GetStats(re); - if (rp->keep_data == 0 || rstats->convertdone == 0 || (rp->keep_data & PR_UPDATE_RENDERSIZE)) { + if ((update_flag & (PR_UPDATE_RENDERSIZE|PR_UPDATE_DATABASE)) || rstats->convertdone == 0) { /* no osa, blur, seq, layers, etc for preview render */ rdata = rp->scene->r; rdata.mode &= ~(R_OSA | R_MBLUR | R_BORDER | R_PANORAMA); @@ -900,11 +899,7 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda RE_SetPixelSize(re, pixsize); - /* database free can crash on a empty Render... */ - if (rp->keep_data == 0 && rstats->convertdone) - RE_Database_Free(re); - - if (rstats->convertdone == 0) { + if ((update_flag & PR_UPDATE_DATABASE) || rstats->convertdone == 0) { unsigned int lay = rp->scene->lay; /* allow localview render for objects with lights in normal layers */ @@ -913,8 +908,20 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda else lay = rp->v3d->lay; RE_SetView(re, rp->viewmat); - + + /* copying blender data while main thread is locked, to avoid crashes */ + WM_job_main_thread_lock_acquire(rp->job); + RE_Database_Free(re); RE_Database_FromScene(re, rp->bmain, rp->scene, lay, 0); // 0= dont use camera view + WM_job_main_thread_lock_release(rp->job); + + /* do preprocessing like building raytree, shadows, volumes, SSS */ + RE_Database_Preprocess(re); + + /* conversion not completed, need to do it again */ + if (!rstats->convertdone) + rp->engine->job_update_flag |= PR_UPDATE_DATABASE; + // printf("dbase update\n"); } else { @@ -932,8 +939,6 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda /* always rotate back */ if (restore) RE_DataBase_IncrementalView(re, rp->viewmat, 1); - - rp->engine->flag &= ~RE_ENGINE_DO_UPDATE; } } @@ -944,21 +949,99 @@ static void render_view3d_free(void *customdata) MEM_freeN(rp); } -static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_data) +static bool render_view3d_flag_changed(RenderEngine *engine, const bContext *C) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + Render *re; + rctf viewplane; + rcti disprect; + float clipsta, clipend; + bool orth; + int job_update_flag = 0; + char name[32]; + + /* ensure render engine exists */ + re = engine->re; + + if (!re) { + sprintf(name, "View3dPreview %p", (void *)ar); + re = engine->re = RE_GetRender(name); + if (!re) + re = engine->re = RE_NewRender(name); + + engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; + } + + /* check update_flag */ + if (engine->update_flag & RE_ENGINE_UPDATE_MA) + job_update_flag |= PR_UPDATE_MATERIAL; + + if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) + job_update_flag |= PR_UPDATE_MATERIAL; + + if (engine->update_flag & RE_ENGINE_UPDATE_DATABASE) { + job_update_flag |= PR_UPDATE_DATABASE; + + /* load editmesh */ + if (scene->obedit) + ED_object_editmode_load(scene->obedit); + } + + engine->update_flag = 0; + + /* check if viewport changed */ + if (engine->last_winx != ar->winx || engine->last_winy != ar->winy) { + engine->last_winx = ar->winx; + engine->last_winy = ar->winy; + job_update_flag |= PR_UPDATE_RENDERSIZE; + } + + if (compare_m4m4(engine->last_viewmat, rv3d->viewmat, 0.00001f) == 0) { + copy_m4_m4(engine->last_viewmat, rv3d->viewmat); + job_update_flag |= PR_UPDATE_VIEW; + } + + render_view3d_get_rects(ar, v3d, rv3d, &viewplane, engine, &clipsta, &clipend, NULL, &orth); + + if (BLI_rctf_compare(&viewplane, &engine->last_viewplane, 0.00001f) == 0) { + engine->last_viewplane = viewplane; + job_update_flag |= PR_UPDATE_VIEW; + } + + render_view3d_disprect(scene, ar, v3d, rv3d, &disprect); + if (BLI_rcti_compare(&disprect, &engine->last_disprect) == 0) { + engine->last_disprect = disprect; + job_update_flag |= PR_UPDATE_RENDERSIZE; + } + + /* any changes? go ahead and rerender */ + if (job_update_flag) { + engine->job_update_flag |= job_update_flag; + return true; + } + + return false; +} + +static void render_view3d_do(RenderEngine *engine, const bContext *C) { wmJob *wm_job; RenderPreview *rp; Scene *scene = CTX_data_scene(C); - if (CTX_wm_window(C) == NULL) { - engine->flag |= RE_ENGINE_DO_UPDATE; + if (CTX_wm_window(C) == NULL) + return; + if (!render_view3d_flag_changed(engine, C)) return; - } wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_wm_region(C), "Render Preview", WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW); rp = MEM_callocN(sizeof(RenderPreview), "render preview"); - + rp->job = wm_job; + /* customdata for preview thread */ rp->scene = scene; rp->engine = engine; @@ -967,7 +1050,6 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d rp->v3d = rp->sa->spacedata.first; rp->rv3d = CTX_wm_region_view3d(C); rp->bmain = CTX_data_main(C); - rp->keep_data = keep_data; copy_m4_m4(rp->viewmat, rp->rv3d->viewmat); /* dont alloc in threads */ @@ -982,80 +1064,33 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d WM_jobs_start(CTX_wm_manager(C), wm_job); engine->flag &= ~RE_ENGINE_DO_UPDATE; - } /* callback for render engine , on changes */ -void render_view3d(RenderEngine *engine, const bContext *C) +void render_view3d_update(RenderEngine *engine, const bContext *C) { - render_view3d_do(engine, C, 0); -} + /* this shouldn't be needed and causes too many database rebuilds, but we + * aren't actually tracking updates for all relevent datablocks so this is + * a catch-all for updates */ + engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; -static int render_view3d_changed(RenderEngine *engine, const bContext *C) -{ - ARegion *ar = CTX_wm_region(C); - Render *re; - int update = 0; - char name[32]; - - sprintf(name, "View3dPreview %p", (void *)ar); - re = RE_GetRender(name); - - if (re) { - RegionView3D *rv3d = CTX_wm_region_view3d(C); - View3D *v3d = CTX_wm_view3d(C); - Scene *scene = CTX_data_scene(C); - rctf viewplane, viewplane1; - rcti disprect, disprect1; - float mat[4][4]; - float clipsta, clipend; - bool orth; - - if (engine->update_flag & RE_ENGINE_UPDATE_MA) - update |= PR_UPDATE_MATERIAL; - - if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) - update |= PR_UPDATE_MATERIAL; - - engine->update_flag = 0; - - if (engine->resolution_x != ar->winx || engine->resolution_y != ar->winy) - update |= PR_UPDATE_RENDERSIZE; - - RE_GetView(re, mat); - if (compare_m4m4(mat, rv3d->viewmat, 0.00001f) == 0) { - update |= PR_UPDATE_VIEW; - } - - render_view3d_get_rects(ar, v3d, rv3d, &viewplane, engine, &clipsta, &clipend, NULL, &orth); - RE_GetViewPlane(re, &viewplane1, &disprect1); - - if (BLI_rctf_compare(&viewplane, &viewplane1, 0.00001f) == 0) - update |= PR_UPDATE_VIEW; - - render_view3d_disprect(scene, ar, v3d, rv3d, &disprect); - if (BLI_rcti_compare(&disprect, &disprect1) == 0) - update |= PR_UPDATE_RENDERSIZE; - - if (update) - engine->flag |= RE_ENGINE_DO_UPDATE; - //if (update) - // printf("changed ma %d res %d view %d\n", update & PR_UPDATE_MATERIAL, update & PR_UPDATE_RENDERSIZE, update & PR_UPDATE_VIEW); - } - - return update; + render_view3d_do(engine, C); } void render_view3d_draw(RenderEngine *engine, const bContext *C) { Render *re = engine->re; RenderResult rres; - int keep_data = render_view3d_changed(engine, C); + char name[32]; - if (engine->flag & RE_ENGINE_DO_UPDATE) - render_view3d_do(engine, C, keep_data); - - if (re == NULL) return; + render_view3d_do(engine, C); + + if (re == NULL) { + sprintf(name, "View3dPreview %p", (void *)CTX_wm_region(C)); + re = RE_GetRender(name); + + if (re == NULL) return; + } RE_AcquireResultImage(re, &rres); @@ -1105,3 +1140,52 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) RE_ReleaseResultImage(re); } + +void ED_viewport_render_kill_jobs(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + Main *bmain = CTX_data_main(C); + bScreen *sc; + ScrArea *sa; + ARegion *ar; + + if (!wm) + return; + + /* kill all actively running jobs */ + WM_jobs_kill(wm, NULL, render_view3d_startjob); + + /* loop over 3D view render engines */ + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + for (sa = sc->areabase.first; sa; sa = sa->next) { + if (sa->spacetype != SPACE_VIEW3D) + continue; + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + RegionView3D *rv3d; + + if (ar->regiontype != RGN_TYPE_WINDOW) + continue; + + rv3d = ar->regiondata; + + if (rv3d->render_engine) { + /* free render database now before we change data, because + * RE_Database_Free will also loop over blender data */ + char name[32]; + Render *re; + + sprintf(name, "View3dPreview %p", (void *)ar); + re = RE_GetRender(name); + + if (re) + RE_Database_Free(re); + + /* tag render engine to update entire database */ + rv3d->render_engine->update_flag |= RE_ENGINE_UPDATE_DATABASE; + } + } + } + } +} + diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 436aef943e4..5f74bf6576a 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -539,7 +539,7 @@ void ED_render_internal_init(void) { RenderEngineType *ret = RE_engines_find("BLENDER_RENDER"); - ret->view_update = render_view3d; + ret->view_update = render_view3d_update; ret->view_draw = render_view3d_draw; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 4ddacc3254e..6c0d5ccd844 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -908,11 +908,19 @@ static void region_overlap_fix(ScrArea *sa, ARegion *ar) /* overlapping regions only in the following restricted cases */ static int region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar) { - if (U.uiflag2 & USER_REGION_OVERLAP) - if (WM_is_draw_triple(win)) - if (ELEM4(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_SEQ, SPACE_CLIP)) + if (U.uiflag2 & USER_REGION_OVERLAP) { + if (WM_is_draw_triple(win)) { + if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { if (ELEM3(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) return 1; + } + else if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) { + if (ELEM4(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW)) + return 1; + } + } + } + return 0; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 553a5cbe9ac..19953723fef 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -2820,6 +2820,8 @@ static void project_paint_begin(ProjPaintState *ps) const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + bool reset_threads = false; + /* ---- end defines ---- */ if (ps->source == PROJ_SRC_VIEW) @@ -3064,6 +3066,10 @@ static void project_paint_begin(ProjPaintState *ps) /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */ + if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) { + reset_threads = true; + } + /* really high values could cause problems since it has to allocate a few * (ps->buckets_x*ps->buckets_y) sized arrays */ CLAMP(ps->buckets_x, PROJ_BUCKET_RECT_MIN, PROJ_BUCKET_RECT_MAX); @@ -3089,6 +3095,11 @@ static void project_paint_begin(ProjPaintState *ps) ps->thread_tot = BKE_scene_num_threads(ps->scene); + /* workaround for #35057, disable threading if diameter is less than is possible for + * optimum bucket number generation */ + if (reset_threads) + ps->thread_tot = 1; + for (a = 0; a < ps->thread_tot; a++) { ps->arena_mt[a] = BLI_memarena_new(1 << 16, "project paint arena"); } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index e10cba43d71..f8166456a2c 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -577,9 +577,10 @@ void snode_set_context(const bContext *C) if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) { - /* invalid tree type, disable */ - snode->tree_idname[0] = '\0'; - ED_node_tree_start(snode, NULL, NULL, NULL); + /* invalid tree type, skip + * NB: not resetting the node path here, invalid bNodeTreeType + * may still be registered at a later point. + */ return; } diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 07fa6997e99..8d6363d3bbe 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -41,6 +41,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_library.h" #include "BKE_screen.h" #include "BKE_node.h" @@ -83,6 +84,8 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) BLI_strncpy(path->node_name, id->name + 2, sizeof(path->node_name)); BLI_addtail(&snode->treepath, path); + + id_us_ensure_real(&ntree->id); } /* update current tree */ @@ -116,6 +119,8 @@ void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode) BLI_addtail(&snode->treepath, path); + id_us_ensure_real(&ntree->id); + /* update current tree */ snode->edittree = ntree; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index d3cf7ae9eea..d5ecdaf0596 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -3273,7 +3273,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D Object *ob = base->object; Mesh *me = ob->data; Material *ma = give_current_material(ob, 1); - const short hasHaloMat = (ma && (ma->material_type == MA_TYPE_HALO)); + const short hasHaloMat = (ma && (ma->material_type == MA_TYPE_HALO) && !BKE_scene_use_new_shading_nodes(scene)); eWireDrawMode draw_wire = OBDRAW_WIRE_OFF; int /* totvert,*/ totedge, totface; DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask); diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index 7f2e5b4b81c..52f87c19dc8 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -59,6 +59,7 @@ #include "ED_mball.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_render.h" #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_util.h" @@ -140,9 +141,12 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) SpaceImage *sima = (SpaceImage *)sa->spacedata.first; if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) { - if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) - if (U.uiflag & USER_GLOBALUNDO) + if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) { + if (U.uiflag & USER_GLOBALUNDO) { + ED_viewport_render_kill_jobs(C); BKE_undo_name(C, undoname); + } + } WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; @@ -192,6 +196,8 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) /* for example, texface stores image pointers */ undo_editmode_clear(); + ED_viewport_render_kill_jobs(C); + if (undoname) BKE_undo_name(C, undoname); else @@ -363,6 +369,8 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op) { int retval; + ED_viewport_render_kill_jobs(C); + if (G.debug & G_DEBUG) printf("redo_cb: operator redo %s\n", op->type->name); ED_undo_pop_op(C, op); @@ -529,6 +537,7 @@ static int undo_history_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } else { + ED_viewport_render_kill_jobs(C); BKE_undo_number(C, item); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); } diff --git a/source/blender/freestyle/intern/python/BPy_ContextFunctions.cpp b/source/blender/freestyle/intern/python/BPy_ContextFunctions.cpp index e44dfdf0bae..dd678ee6fbd 100644 --- a/source/blender/freestyle/intern/python/BPy_ContextFunctions.cpp +++ b/source/blender/freestyle/intern/python/BPy_ContextFunctions.cpp @@ -79,6 +79,26 @@ ContextFunctions_get_canvas_height(PyObject *self) return PyLong_FromLong(ContextFunctions::GetCanvasHeightCF()); } +static char ContextFunctions_get_border___doc__[] = +".. method:: get_border()\n" +"\n" +" Returns the border.\n" +"\n" +" :return: A tuple of 4 numbers (xmin, ymin, xmax, ymax).\n" +" :rtype: tuple\n"; + +static PyObject * +ContextFunctions_get_border(PyObject *self) +{ + BBox border(ContextFunctions::GetBorderCF()); + PyObject *v = PyTuple_New(4); + PyTuple_SET_ITEM(v, 0, PyLong_FromLong(border.getMin().x())); + PyTuple_SET_ITEM(v, 1, PyLong_FromLong(border.getMin().y())); + PyTuple_SET_ITEM(v, 2, PyLong_FromLong(border.getMax().x())); + PyTuple_SET_ITEM(v, 3, PyLong_FromLong(border.getMax().y())); + return v; +} + static char ContextFunctions_load_map___doc__[] = ".. function:: load_map(file_name, map_name, num_levels=4, sigma=1.0)\n" "\n" @@ -232,6 +252,8 @@ static PyMethodDef module_functions[] = { ContextFunctions_get_canvas_width___doc__}, {"get_canvas_height", (PyCFunction)ContextFunctions_get_canvas_height, METH_NOARGS, ContextFunctions_get_canvas_height___doc__}, + {"get_border", (PyCFunction)ContextFunctions_get_border, METH_NOARGS, + ContextFunctions_get_border___doc__}, {"load_map", (PyCFunction)ContextFunctions_load_map, METH_VARARGS | METH_KEYWORDS, ContextFunctions_load_map___doc__}, {"read_map_pixel", (PyCFunction)ContextFunctions_read_map_pixel, METH_VARARGS | METH_KEYWORDS, diff --git a/source/blender/freestyle/intern/stroke/Canvas.h b/source/blender/freestyle/intern/stroke/Canvas.h index 07aa171534a..038b4b3af0f 100644 --- a/source/blender/freestyle/intern/stroke/Canvas.h +++ b/source/blender/freestyle/intern/stroke/Canvas.h @@ -195,6 +195,7 @@ public: virtual int width() const = 0; virtual int height() const = 0; + virtual BBox border() const = 0; virtual BBox scene3DBBox() const = 0; inline const StrokeRenderer *renderer() const diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.cpp b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp index 42d27a0e5c3..052a6804815 100644 --- a/source/blender/freestyle/intern/stroke/ContextFunctions.cpp +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp @@ -51,6 +51,11 @@ unsigned GetCanvasHeightCF() return Canvas::getInstance()->height(); } +BBox GetBorderCF() +{ + return Canvas::getInstance()->border(); +} + void LoadMapCF(const char *iFileName, const char *iMapName, unsigned iNbLevels, float iSigma) { return Canvas::getInstance()->loadMap(iFileName, iMapName, iNbLevels, iSigma); diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.h b/source/blender/freestyle/intern/stroke/ContextFunctions.h index 5c0b88fd412..28ce918e919 100644 --- a/source/blender/freestyle/intern/stroke/ContextFunctions.h +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.h @@ -54,10 +54,15 @@ LIB_STROKE_EXPORT unsigned GetCanvasWidthCF(); // GetCanvasHeight -/*! Returns the canvas width */ +/*! Returns the canvas height */ LIB_STROKE_EXPORT unsigned GetCanvasHeightCF(); +// GetBorder +/*! Returns the border */ +LIB_STROKE_EXPORT +BBox GetBorderCF(); + // Load map /*! Loads an image map for further reading */ LIB_STROKE_EXPORT diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index f67561954be..5499386dcf1 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -6385,6 +6385,15 @@ bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, bool i return equals; } + case PROP_POINTER: + { + if (!STREQ(RNA_property_identifier(prop), "rna_type")) { + PointerRNA propptr_a = RNA_property_pointer_get(a, prop); + PointerRNA propptr_b = RNA_property_pointer_get(b, prop); + return RNA_struct_equals(&propptr_a, &propptr_b, is_strict); + } + } + default: break; } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 85bb8125a3a..fc4b78b5c05 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1531,7 +1531,7 @@ void IDP_spit(IDProperty *prop) ret_str = PyObject_Repr(ret_dict); Py_DECREF(ret_dict); - printf("IDProperty: %s\n", _PyUnicode_AsString(ret_str)); + printf("IDProperty(%p): %s\n", prop, _PyUnicode_AsString(ret_str)); Py_DECREF(ret_str); diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index ee5f6109ab8..b8907e797ef 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -66,7 +66,7 @@ typedef void (*RNA_SetIndexFunc)(PointerRNA *, PropertyRNA *, int index, void *) /* arr[3] = x, self->arraydim is 0, lvalue_dim is 1 */ /* Ensures that a python sequence has expected number of items/sub-items and items are of desired type. */ -static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[], +static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[], const bool is_dynamic, ItemTypeCheckFunc check_item_type, const char *item_type_str, const char *error_prefix) { Py_ssize_t i; @@ -110,7 +110,9 @@ static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[] error_prefix, dim + 1, dimsize[dim + 1], item_seq_size); ok = 0; } - else if (validate_array_type(item, dim + 1, totdim, dimsize, check_item_type, item_type_str, error_prefix) == -1) { + else if (validate_array_type(item, dim + 1, totdim, dimsize, is_dynamic, + check_item_type, item_type_str, error_prefix) == -1) + { ok = 0; } @@ -129,7 +131,7 @@ static int validate_array_type(PyObject *seq, int dim, int totdim, int dimsize[] error_prefix, dim + 1, Py_TYPE(seq)->tp_name); return -1; } - else if (seq_size != dimsize[dim]) { + else if ((seq_size != dimsize[dim]) && (is_dynamic == false)) { PyErr_Format(PyExc_ValueError, "%s sequences of dimension %d should contain %d items, not %d", error_prefix, dim, dimsize[dim], seq_size); return -1; @@ -315,8 +317,12 @@ static int validate_array(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, { - if (validate_array_type(rvalue, lvalue_dim, totdim, dimsize, check_item_type, item_type_str, error_prefix) == -1) + const int prop_flag = RNA_property_flag(prop); + if (validate_array_type(rvalue, lvalue_dim, totdim, dimsize, (prop_flag & PROP_DYNAMIC) != 0, + check_item_type, item_type_str, error_prefix) == -1) + { return -1; + } return validate_array_length(rvalue, ptr, prop, lvalue_dim, totitem, error_prefix); } diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index b6c3668512b..786c67f5305 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -70,6 +70,7 @@ struct Scene; /* RenderEngine.update_flag, used by internal now */ #define RE_ENGINE_UPDATE_MA 1 #define RE_ENGINE_UPDATE_OTHER 2 +#define RE_ENGINE_UPDATE_DATABASE 4 extern ListBase R_engines; @@ -97,7 +98,7 @@ typedef struct RenderEngine { RenderEngineType *type; void *py_instance; - int flag, update_flag; + int flag; struct Object *camera_override; int tile_x; @@ -110,6 +111,15 @@ typedef struct RenderEngine { int resolution_x, resolution_y; struct ReportList *reports; + + /* for blender internal only */ + int update_flag; + int job_update_flag; + + rctf last_viewplane; + rcti last_disprect; + float last_viewmat[4][4]; + int last_winx, last_winy; } RenderEngine; RenderEngine *RE_engine_create(RenderEngineType *type); diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index da53bc5a819..e154fd42119 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -207,6 +207,7 @@ void RE_GetViewPlane(struct Render *re, rctf *viewplane, rcti *disprect); /* make or free the dbase */ void RE_Database_FromScene(struct Render *re, struct Main *bmain, struct Scene *scene, unsigned int lay, int use_camera_view); +void RE_Database_Preprocess(struct Render *re); void RE_Database_Free(struct Render *re); /* project dbase again, when viewplane/perspective changed */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index 6e70b4bcfc9..ce71ab188a9 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -4863,7 +4863,11 @@ static void init_render_object(Render *re, Object *ob, Object *par, DupliObject void RE_Database_Free(Render *re) { LampRen *lar; - + + /* will crash if we try to free empty database */ + if (!re->i.convertdone) + return; + /* statistics for debugging render memory usage */ if ((G.debug & G_DEBUG) && (G.is_rendering)) { if ((re->r.scemode & (R_BUTS_PREVIEW|R_VIEWPORT_PREVIEW))==0) { @@ -5321,14 +5325,10 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l database_init_objects(re, lay, 0, 0, 0, 0); if (!re->test_break(re->tbh)) { - int tothalo; - set_material_lightgroups(re); for (sce= re->scene; sce; sce= sce->set) set_renderlayer_lightgroups(re, sce); - slurph_opt= 1; - /* for now some clumsy copying still */ re->i.totvert= re->totvert; re->i.totface= re->totvlak; @@ -5336,7 +5336,16 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l re->i.tothalo= re->tothalo; re->i.totlamp= re->totlamp; re->stats_draw(re->sdh, &re->i); - + } + + slurph_opt= 1; +} + +void RE_Database_Preprocess(Render *re) +{ + if (!re->test_break(re->tbh)) { + int tothalo; + /* don't sort stars */ tothalo= re->tothalo; if (!re->test_break(re->tbh)) { @@ -5392,11 +5401,12 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l if (!re->test_break(re->tbh)) if (re->r.mode & R_RAYTRACE) volume_precache(re); - } - if (re->test_break(re->tbh)) + if (re->test_break(re->tbh)) { + re->i.convertdone = TRUE; RE_Database_Free(re); + } else re->i.convertdone = TRUE; @@ -5866,8 +5876,10 @@ void RE_Database_FromScene_Vectors(Render *re, Main *bmain, Scene *sce, unsigned RE_Database_Free(re); re->strandsurface= strandsurface; - if (!re->test_break(re->tbh)) + if (!re->test_break(re->tbh)) { RE_Database_FromScene(re, bmain, sce, lay, 1); + RE_Database_Preprocess(re); + } if (!re->test_break(re->tbh)) { int vectorlay= get_vector_renderlayers(re->scene); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index f7d347e8a5a..848e94c8d4b 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1142,10 +1142,13 @@ static void do_render_3d(Render *re) re->draw_lock(re->dlh, 1); /* make render verts/faces/halos/lamps */ - if (render_scene_needs_vector(re)) + if (render_scene_needs_vector(re)) { RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); - else + } + else { RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); + RE_Database_Preprocess(re); + } /* clear UI drawing locks */ if (re->draw_lock) @@ -1676,6 +1679,9 @@ static void add_freestyle(Render *re, int render) } FRS_finish_stroke_rendering(re); + + /* restore the global R value (invalidated by nested execution of the internal renderer) */ + R = *re; } /* merges the results of Freestyle stroke rendering into a given render result */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 230784d7273..7cb24daaaef 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -404,6 +404,9 @@ void WM_jobs_kill_type(struct wmWindowManager *wm, int job_type); int WM_jobs_has_running(struct wmWindowManager *wm); +void WM_job_main_thread_lock_acquire(struct wmJob *job); +void WM_job_main_thread_lock_release(struct wmJob *job); + /* clipboard */ char *WM_clipboard_text_get(int selection); void WM_clipboard_text_set(char *buf, int selection); diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index 03af5e9e8a6..c6e067dc2f9 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -128,8 +128,43 @@ struct wmJob { ListBase threads; double start_time; + + /* ticket mutex for main thread locking while some job accesses + * data that the main thread might modify at the same time */ + TicketMutex *main_thread_mutex; + bool main_thread_mutex_ending; }; +/* Main thread locking */ + +void WM_job_main_thread_lock_acquire(wmJob *wm_job) +{ + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); + + /* if BLI_end_threads is being called to stop the job before it's finished, + * we no longer need to lock to get access to the main thread as it's + * waiting and can't respond */ + if (wm_job->main_thread_mutex_ending) + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); +} + +void WM_job_main_thread_lock_release(wmJob *wm_job) +{ + if (!wm_job->main_thread_mutex_ending) + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); +} + +static void wm_job_main_thread_yield(wmJob *wm_job, bool ending) +{ + if (ending) + wm_job->main_thread_mutex_ending = true; + + /* unlock and lock the ticket mutex. because it's a fair mutex any job that + * is waiting to acquire the lock will get it first, before we can lock */ + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); +} + /* finds: * if type, compare for it, otherwise any matching job */ @@ -162,13 +197,16 @@ wmJob *WM_jobs_get(wmWindowManager *wm, wmWindow *win, void *owner, const char * if (wm_job == NULL) { wm_job = MEM_callocN(sizeof(wmJob), "new job"); - + BLI_addtail(&wm->jobs, wm_job); wm_job->win = win; wm_job->owner = owner; wm_job->flag = flag; wm_job->job_type = job_type; BLI_strncpy(wm_job->name, name, sizeof(wm_job->name)); + + wm_job->main_thread_mutex = BLI_ticket_mutex_alloc(); + BLI_ticket_mutex_lock(wm_job->main_thread_mutex); } /* else: a running job, be careful */ @@ -369,12 +407,21 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job) } } +static void wm_job_free(wmWindowManager *wm, wmJob *wm_job) +{ + BLI_remlink(&wm->jobs, wm_job); + BLI_ticket_mutex_unlock(wm_job->main_thread_mutex); + BLI_ticket_mutex_free(wm_job->main_thread_mutex); + MEM_freeN(wm_job); +} + /* stop job, end thread, free data completely */ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job) { if (wm_job->running) { /* signal job to end */ wm_job->stop = TRUE; + wm_job_main_thread_yield(wm_job, true); BLI_end_threads(&wm_job->threads); if (wm_job->endjob) @@ -389,9 +436,7 @@ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job) wm_job->run_free(wm_job->run_customdata); /* remove wm_job */ - BLI_remlink(&wm->jobs, wm_job); - MEM_freeN(wm_job); - + wm_job_free(wm, wm_job); } /* wait until every job ended */ @@ -483,7 +528,6 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) float total_progress = 0.f; float jobs_progress = 0; - for (wm_job = wm->jobs.first; wm_job; wm_job = wm_jobnext) { wm_jobnext = wm_job->next; @@ -491,6 +535,9 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) /* running threads */ if (wm_job->threads.first) { + + /* let threads get temporary lock over main thread if needed */ + wm_job_main_thread_yield(wm_job, false); /* always call note and update when ready */ if (wm_job->do_update || wm_job->ready) { @@ -522,7 +569,9 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) } wm_job->running = FALSE; + wm_job_main_thread_yield(wm_job, true); BLI_end_threads(&wm_job->threads); + wm_job->main_thread_mutex_ending = false; if (wm_job->endnote) WM_event_add_notifier(C, wm_job->endnote, NULL); @@ -539,8 +588,7 @@ void wm_jobs_timer(const bContext *C, wmWindowManager *wm, wmTimer *wt) wm_job->wt = NULL; /* remove wm_job */ - BLI_remlink(&wm->jobs, wm_job); - MEM_freeN(wm_job); + wm_job_free(wm, wm_job); } } else if (wm_job->flag & WM_JOB_PROGRESS) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index e6a833a8a87..b9a9d6b1957 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1880,6 +1880,7 @@ static void WM_OT_userpref_autoexec_path_add(wmOperatorType *ot) { ot->name = "Add Autoexec Path"; ot->idname = "WM_OT_userpref_autoexec_path_add"; + ot->description = "Add path to exclude from autoexecution"; ot->exec = wm_userpref_autoexec_add_exec; ot->poll = WM_operator_winactive; @@ -1901,6 +1902,7 @@ static void WM_OT_userpref_autoexec_path_remove(wmOperatorType *ot) { ot->name = "Remove Autoexec Path"; ot->idname = "WM_OT_userpref_autoexec_path_remove"; + ot->description = "Remove path to exclude from autoexecution"; ot->exec = wm_userpref_autoexec_remove_exec; ot->poll = WM_operator_winactive;