From 15a2287a8efeed84aa09db9e65dde36d4d1f9d0f Mon Sep 17 00:00:00 2001 From: Tamito Kajiyama Date: Sun, 7 Jul 2013 15:29:00 +0000 Subject: [PATCH 01/24] Fix for incorrect clipping of Freestyle strokes when the viewport preview is used. --- .../style_modules/parameter_editor.py | 14 ++---------- .../intern/python/BPy_ContextFunctions.cpp | 22 +++++++++++++++++++ .../blender/freestyle/intern/stroke/Canvas.h | 1 + .../intern/stroke/ContextFunctions.cpp | 5 +++++ .../intern/stroke/ContextFunctions.h | 7 +++++- 5 files changed, 36 insertions(+), 13 deletions(-) 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/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 From 22f26b2ce4c75a562ce31d6633b7343acfd7f94a Mon Sep 17 00:00:00 2001 From: Tamito Kajiyama Date: Sun, 7 Jul 2013 17:24:30 +0000 Subject: [PATCH 02/24] Fix for Bug #35970: Freestyle + Particles = Crash. The crash was caused by a reference of freed memory in add_halo_flare() that relied on the global variable R to retrieve a list of objects. The value of this variable is invalidated by a nested execution of the Blender Internal renderer for Freestyle stroke rendering, leading to the documented memory reference issue. --- source/blender/render/intern/source/pipeline.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index f7d347e8a5a..dfdfe973241 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1676,6 +1676,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 */ From 7d2030d23378cd4a6f0f0be0997324d67b3e642a Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Sun, 7 Jul 2013 18:29:57 +0000 Subject: [PATCH 03/24] Arrempt to fix Cycles compilation with Clang Issue is caused by missing sse flags for Clang compilers, this flags only was set for GNU C compilers. Added if branch for Clang now, which contains the same flags apart from -mfpmath=sse, This is because Clang was claiming it's unused argument. Probably OSX would need some further checks since it's also using Clang. I've got no idea why it could have worked for OSX before.. --- intern/cycles/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) 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 From 2c8087aa2a9853f9caabc052b2e753c28704172b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 00:51:30 +0000 Subject: [PATCH 04/24] fix [#36047] Recalculate normals produces faulty normals on certain simple meshes The mesh in the report had 3 faces-user-edges, resolve the problem by not walking over these edges. also don't recurse anymore (avoids realloc's). --- source/blender/bmesh/operators/bmo_utils.c | 156 ++++++++++----------- 1 file changed, 76 insertions(+), 80 deletions(-) diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index 5e61b8ea7ea..876e4a9fedc 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -282,125 +282,121 @@ void bmo_region_extend_exec(BMesh *bm, BMOperator *op) #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. */ + * in case all faces were not done: start over with 'find the ultimate ...' */ /* 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; + const bool use_face_tag = BMO_slot_bool_get(op->slots_in, "use_face_tag"); + const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); + unsigned int tot_touch = 0; 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) { + fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); - /* clear dirty flag */ - BM_elem_flag_disable(f, BM_ELEM_TAG); + while (tot_touch != tot_faces) { + BMOIter siter; + float f_len_best = -FLT_MAX; + BMFace *f, *f_start = NULL; + float f_start_cent[3]; - if (BMO_elem_flag_test(bm, f, FACE_VIS)) - continue; + /* find a starting face */ + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + float f_cent[3]; + float f_len_test; - if (!startf) startf = f; + /* clear dirty flag */ + BM_elem_flag_disable(f, BM_ELEM_TAG); - BM_face_calc_center_bounds(f, cent); + if (BMO_elem_flag_test(bm, f, FACE_VIS)) + continue; - if ((maxx_test = dot_v3v3(cent, cent)) > maxx) { - maxx = maxx_test; - startf = f; + if (!f_start) f_start = f; + + BM_face_calc_center_bounds(f, f_cent); + + if ((f_len_test = len_squared_v3(f_cent)) > f_len_best) { + f_len_best = f_len_test; + f_start = f; + copy_v3_v3(f_start_cent, f_cent); + } } - } - if (!startf) return; + /* check sanity (while loop ensures) */ + BLI_assert(f_start != NULL); - BM_face_calc_center_bounds(startf, cent); + /* make sure the starting face has the correct winding */ + if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { + BM_face_normal_flip(bm, f_start); + BMO_elem_flag_toggle(bm, f_start, FACE_FLIP); - /* 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_face_tag) { + BM_elem_flag_toggle(f_start, BM_ELEM_TAG); + } + } - 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); + /* 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); - 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) + STACK_PUSH(fstack, f_start); + BMO_elem_flag_enable(bm, f_start, FACE_VIS); + tot_touch++; + + while ((f = STACK_POP(fstack))) { + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_other = l->radial_next; + + if ((l_other == l) || l_other->radial_next != l) { continue; + } - if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) { - BMO_elem_flag_enable(bm, l2->f, FACE_VIS); + if (BMO_elem_flag_test(bm, l_other->f, FACE_FLAG)) { + if (!BMO_elem_flag_test(bm, l_other->f, FACE_VIS)) { + BMO_elem_flag_enable(bm, l_other->f, FACE_VIS); + tot_touch++; - 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); + + if (l_other->v == l->v) { + BM_face_normal_flip(bm, l_other->f); + + BMO_elem_flag_toggle(bm, l_other->f, FACE_FLIP); + if (use_face_tag) { + BM_elem_flag_toggle(l_other->f, BM_ELEM_TAG); + } } + else if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + if (use_face_tag) { + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + BM_elem_flag_disable(l_other->f, BM_ELEM_TAG); + } + } + + STACK_PUSH(fstack, l_other->f); } - - 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) From 37d1984c53870c28c3c27165cba11f4f039706af Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 01:28:43 +0000 Subject: [PATCH 05/24] edit on own commit r57801, don't check the size of dynamic arrays --- source/blender/python/intern/bpy_rna_array.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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); } From 2c87b8818058c02e32c92f259df5659c5b5c9eeb Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 07:25:33 +0000 Subject: [PATCH 06/24] re-arrange space_userpref_keymap, no need to use a subclass here, just import the module and call draw_keymaps(). - making this change now because some addons developers are interested in showing keymap UI in their addons prefs and using the class involved making a fake class instance which isnt needed. --- release/scripts/startup/bl_ui/__init__.py | 1 - .../scripts/startup/bl_ui/space_userpref.py | 25 +- .../startup/bl_ui/space_userpref_keymap.py | 677 +++++++++--------- 3 files changed, 355 insertions(+), 348 deletions(-) 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..a5e4b6e10bf 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 bl_ui.space_userpref_keymap 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 index 8d6eb2c623d..727e9935bcc 100644 --- a/release/scripts/startup/bl_ui/space_userpref_keymap.py +++ b/release/scripts/startup/bl_ui/space_userpref_keymap.py @@ -17,375 +17,368 @@ # ##### END GPL LICENSE BLOCK ##### # + +__all__ = ( + "draw_entry", + "draw_km", + "draw_kmi", + "draw_filtered", + "draw_hierarchy", + "draw_keymaps", + ) + + 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 _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 - 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) + split = layout.split(percentage=indent) + col = split.column() + col = split.column() + return col -class InputKeyMapPanel: - bl_space_type = 'USER_PREFERENCES' - bl_label = "Input" - bl_region_type = 'WINDOW' - bl_options = {'HIDE_HEADER'} +def draw_entry(display_keymaps, entry, col, level=0): + idname, spaceid, regionid, children = entry - 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: + draw_km(display_keymaps, kc, km, children, col, level) - 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 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) - ''' + if km: + 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(display_keymaps, kc, km, children, layout, level): + km = km.active() - def draw_km(self, display_keymaps, kc, km, children, layout, level): - km = km.active() + layout.context_pointer_set("keymap", km) - layout.context_pointer_set("keymap", km) + col = _indented_layout(layout, level) - 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 = 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() - 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.is_modal: - row.label(text="", icon='LINKED') - if km.is_user_modified: - row.operator("wm.keymap_restore", text="Restore") + 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: - row.label() + km.show_expanded_items = True - 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: + draw_kmi(display_keymaps, kc, km, kmi, col, level + 1) - # 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! + # "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() - 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) + + # 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: - self.draw_hierarchy(display_keymaps, col) - ok = True + # One day... + #~ sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="") + sub.prop(kmi, "idname", text="") - # 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 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 = {} -if __name__ == "__main__": # only for live edit. - bpy.utils.register_module(__name__) +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') From 8a388a7bef531cfdbf2b33cca233dfeac0b7a211 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 07:40:32 +0000 Subject: [PATCH 07/24] fix [#36043] Missing Tooltip for Excluded Paths field of Auto Run Python Scripts in User Preferences -> File also select more/less were flipped for mesh menu compared to everywhere else. --- release/scripts/startup/bl_ui/space_view3d.py | 2 +- source/blender/windowmanager/intern/wm_operators.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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/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; From 96d78e3de96f876fde8644160dc641ebc7a58e87 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 8 Jul 2013 08:02:37 +0000 Subject: [PATCH 08/24] Fix #36042: Subdividing a cyclic spline shifts start/end points Issue goes back to 2.4x days at least (but very much likely the issue is even older). It's caused by subdivision code was starting to iterate points from previous one, which shifted all the points by one. Reshuffled code so now iteration starts from first spline point. Thanks to Campbell for review and tests! :) --- source/blender/editors/curve/editcurve.c | 165 +++++++++++++---------- 1 file changed, 94 insertions(+), 71 deletions(-) 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; From 335fd7388f601e0db347cc64a202c9a166943a57 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 08:50:04 +0000 Subject: [PATCH 09/24] move normal recalculation into its own source file. --- source/blender/bmesh/CMakeLists.txt | 1 + source/blender/bmesh/operators/bmo_normals.c | 156 +++++++++++++++++++ source/blender/bmesh/operators/bmo_utils.c | 121 -------------- 3 files changed, 157 insertions(+), 121 deletions(-) create mode 100644 source/blender/bmesh/operators/bmo_normals.c 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/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c new file mode 100644 index 00000000000..d238644fb02 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -0,0 +1,156 @@ +/* + * ***** 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_VIS 1 +#define FACE_FLAG 2 +#define FACE_FLIP 8 + +/* + * 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 ...' */ + +/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ + +void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) +{ + BMFace **fstack; + STACK_DECLARE(fstack); + const bool use_face_tag = BMO_slot_bool_get(op->slots_in, "use_face_tag"); + const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); + unsigned int tot_touch = 0; + + BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_FLAG); + + fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); + + while (tot_touch != tot_faces) { + BMOIter siter; + float f_len_best = -FLT_MAX; + BMFace *f, *f_start = NULL; + float f_start_cent[3]; + + /* find a starting face */ + BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { + float f_cent[3]; + float f_len_test; + + /* clear dirty flag */ + BM_elem_flag_disable(f, BM_ELEM_TAG); + + if (BMO_elem_flag_test(bm, f, FACE_VIS)) + continue; + + if (!f_start) f_start = f; + + BM_face_calc_center_bounds(f, f_cent); + + if ((f_len_test = len_squared_v3(f_cent)) > f_len_best) { + f_len_best = f_len_test; + f_start = f; + copy_v3_v3(f_start_cent, f_cent); + } + } + + /* check sanity (while loop ensures) */ + BLI_assert(f_start != NULL); + + /* make sure the starting face has the correct winding */ + if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { + BM_face_normal_flip(bm, f_start); + BMO_elem_flag_toggle(bm, f_start, FACE_FLIP); + + if (use_face_tag) { + BM_elem_flag_toggle(f_start, 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). */ + STACK_INIT(fstack); + + STACK_PUSH(fstack, f_start); + BMO_elem_flag_enable(bm, f_start, FACE_VIS); + tot_touch++; + + while ((f = STACK_POP(fstack))) { + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_other = l->radial_next; + + if ((l_other == l) || l_other->radial_next != l) { + continue; + } + + if (BMO_elem_flag_test(bm, l_other->f, FACE_FLAG)) { + if (!BMO_elem_flag_test(bm, l_other->f, FACE_VIS)) { + BMO_elem_flag_enable(bm, l_other->f, FACE_VIS); + tot_touch++; + + + if (l_other->v == l->v) { + BM_face_normal_flip(bm, l_other->f); + + BMO_elem_flag_toggle(bm, l_other->f, FACE_FLIP); + if (use_face_tag) { + BM_elem_flag_toggle(l_other->f, BM_ELEM_TAG); + } + } + else if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + if (use_face_tag) { + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + BM_elem_flag_disable(l_other->f, BM_ELEM_TAG); + } + } + + STACK_PUSH(fstack, l_other->f); + } + } + } + } + } + + MEM_freeN(fstack); +} diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c index 876e4a9fedc..2a0a7864499 100644 --- a/source/blender/bmesh/operators/bmo_utils.c +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -278,127 +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_FLIP 8 - -/* - * 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 ...' */ - -/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ - -void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) -{ - BMFace **fstack; - STACK_DECLARE(fstack); - const bool use_face_tag = BMO_slot_bool_get(op->slots_in, "use_face_tag"); - const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); - unsigned int tot_touch = 0; - - BMO_slot_buffer_flag_enable(bm, op->slots_in, "faces", BM_FACE, FACE_FLAG); - - fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); - - while (tot_touch != tot_faces) { - BMOIter siter; - float f_len_best = -FLT_MAX; - BMFace *f, *f_start = NULL; - float f_start_cent[3]; - - /* find a starting face */ - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - float f_cent[3]; - float f_len_test; - - /* clear dirty flag */ - BM_elem_flag_disable(f, BM_ELEM_TAG); - - if (BMO_elem_flag_test(bm, f, FACE_VIS)) - continue; - - if (!f_start) f_start = f; - - BM_face_calc_center_bounds(f, f_cent); - - if ((f_len_test = len_squared_v3(f_cent)) > f_len_best) { - f_len_best = f_len_test; - f_start = f; - copy_v3_v3(f_start_cent, f_cent); - } - } - - /* check sanity (while loop ensures) */ - BLI_assert(f_start != NULL); - - /* make sure the starting face has the correct winding */ - if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { - BM_face_normal_flip(bm, f_start); - BMO_elem_flag_toggle(bm, f_start, FACE_FLIP); - - if (use_face_tag) { - BM_elem_flag_toggle(f_start, 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). */ - STACK_INIT(fstack); - - STACK_PUSH(fstack, f_start); - BMO_elem_flag_enable(bm, f_start, FACE_VIS); - tot_touch++; - - while ((f = STACK_POP(fstack))) { - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_other = l->radial_next; - - if ((l_other == l) || l_other->radial_next != l) { - continue; - } - - if (BMO_elem_flag_test(bm, l_other->f, FACE_FLAG)) { - if (!BMO_elem_flag_test(bm, l_other->f, FACE_VIS)) { - BMO_elem_flag_enable(bm, l_other->f, FACE_VIS); - tot_touch++; - - - if (l_other->v == l->v) { - BM_face_normal_flip(bm, l_other->f); - - BMO_elem_flag_toggle(bm, l_other->f, FACE_FLIP); - if (use_face_tag) { - BM_elem_flag_toggle(l_other->f, BM_ELEM_TAG); - } - } - else if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { - if (use_face_tag) { - BM_elem_flag_disable(l->f, BM_ELEM_TAG); - BM_elem_flag_disable(l_other->f, BM_ELEM_TAG); - } - } - - STACK_PUSH(fstack, l_other->f); - } - } - } - } - } - - MEM_freeN(fstack); -} - void bmo_smooth_vert_exec(BMesh *UNUSED(bm), BMOperator *op) { BMOIter siter; From 61bbefe40d5a1127c06c7fb9efb91a0241ebc410 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 08:56:46 +0000 Subject: [PATCH 10/24] remove unused normal-recalc flags. --- source/blender/bmesh/intern/bmesh_opdefines.c | 1 - source/blender/bmesh/operators/bmo_normals.c | 18 ------------------ source/blender/editors/mesh/editmesh_tools.c | 2 +- 3 files changed, 1 insertion(+), 20 deletions(-) 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/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index d238644fb02..88a4d4478be 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -38,7 +38,6 @@ #define FACE_VIS 1 #define FACE_FLAG 2 -#define FACE_FLIP 8 /* * put normal to the outside, and set the first direction flags in edges @@ -54,7 +53,6 @@ void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) { BMFace **fstack; STACK_DECLARE(fstack); - const bool use_face_tag = BMO_slot_bool_get(op->slots_in, "use_face_tag"); const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); unsigned int tot_touch = 0; @@ -96,11 +94,6 @@ void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) /* make sure the starting face has the correct winding */ if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { BM_face_normal_flip(bm, f_start); - BMO_elem_flag_toggle(bm, f_start, FACE_FLIP); - - if (use_face_tag) { - BM_elem_flag_toggle(f_start, BM_ELEM_TAG); - } } /* now that we've found our starting face, make all connected faces @@ -132,17 +125,6 @@ void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) if (l_other->v == l->v) { BM_face_normal_flip(bm, l_other->f); - - BMO_elem_flag_toggle(bm, l_other->f, FACE_FLIP); - if (use_face_tag) { - BM_elem_flag_toggle(l_other->f, BM_ELEM_TAG); - } - } - else if (BM_elem_flag_test(l_other->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { - if (use_face_tag) { - BM_elem_flag_disable(l->f, BM_ELEM_TAG); - BM_elem_flag_disable(l_other->f, BM_ELEM_TAG); - } } STACK_PUSH(fstack, l_other->f); 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")) From fa7e690a91258cfcfd266d48a4b084ba734d7b5f Mon Sep 17 00:00:00 2001 From: Lukas Toenne Date: Mon, 8 Jul 2013 11:38:09 +0000 Subject: [PATCH 11/24] Partial fix for #36024, don't always reset the node editor tree pointer if the type is undefined. This can happen if the tree type is defined by addon or script, in which case the tree type would be unknown the first time the context is checked, but registered right afterward. Also unknown tree types are handled fine, they just display dummy nodes in red warning color. --- source/blender/editors/space_node/node_edit.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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; } From d75e0f320b4af13d5a35c5787a8ff992b959149e Mon Sep 17 00:00:00 2001 From: Lukas Toenne Date: Mon, 8 Jul 2013 11:38:11 +0000 Subject: [PATCH 12/24] Fix #36024. This part fixes user counting for node editor trees. The user count previously would only work correctly for node trees which are part of material, scene, etc. or linked by group nodes. Any custom pynodes tree edited directly as library data would get a 0 user count on reload and subsequently not be saved. Now the node space follows the same pattern as the image space: the node tree(s) user count gets incremented on file load and opening in the editor ensures a real user. This leads to 1 "unreal" user for the editor (dropped on reload), but seems to be the only viable solution atm. --- source/blender/blenloader/intern/readfile.c | 9 +++++---- source/blender/editors/space_node/space_node.c | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) 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/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; From 0a006cce9cdf71aa02cd5cc0b8c13d046550f217 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Mon, 8 Jul 2013 13:02:21 +0000 Subject: [PATCH 13/24] Fix bevel bugs 34445 and 35109, copying over edge data. The bugs were about not respecting edge smoothness and not respecting edge crease. This change copies the edge attributes from a beveled edge to the two outside edges of the bevel. --- source/blender/bmesh/tools/bmesh_bevel.c | 8 ++++++++ 1 file changed, 8 insertions(+) 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); } /* From 27734f5bec7b717e82128e637c805b95e2b0f889 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 13:30:11 +0000 Subject: [PATCH 14/24] fix/improve normal calculation, noticed when checking on the previous bugfix. - normals depended on the meshes rotation, so you could rotate Suzzane and in some cases one of the eye normals would be flipped. - normals depended on the meshes placement in relation to the meshes center, now find the outer most face by each face-island center. --- source/blender/bmesh/intern/bmesh_queries.c | 113 +++++++++++ source/blender/bmesh/intern/bmesh_queries.h | 2 + source/blender/bmesh/operators/bmo_normals.c | 203 +++++++++++-------- 3 files changed, 239 insertions(+), 79 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index be186e0441b..c0c4e0f0209 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 edges. + * - 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 index 88a4d4478be..7116ef82a32 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -36,8 +36,104 @@ /********* righthand faces implementation ****** */ -#define FACE_VIS 1 -#define FACE_FLAG 2 +#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 @@ -47,92 +143,41 @@ * * in case all faces were not done: start over with 'find the ultimate ...' */ -/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ - void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) { - BMFace **fstack; - STACK_DECLARE(fstack); - const unsigned int tot_faces = BMO_slot_buffer_count(op->slots_in, "faces"); - unsigned int tot_touch = 0; + 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); - fstack = MEM_mallocN(sizeof(*fstack) * tot_faces, __func__); + 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; - while (tot_touch != tot_faces) { - BMOIter siter; - float f_len_best = -FLT_MAX; - BMFace *f, *f_start = NULL; - float f_start_cent[3]; - - /* find a starting face */ - BMO_ITER (f, &siter, op->slots_in, "faces", BM_FACE) { - float f_cent[3]; - float f_len_test; - - /* clear dirty flag */ - BM_elem_flag_disable(f, BM_ELEM_TAG); - - if (BMO_elem_flag_test(bm, f, FACE_VIS)) - continue; - - if (!f_start) f_start = f; - - BM_face_calc_center_bounds(f, f_cent); - - if ((f_len_test = len_squared_v3(f_cent)) > f_len_best) { - f_len_best = f_len_test; - f_start = f; - copy_v3_v3(f_start_cent, f_cent); - } + for (j = 0; j < fg_len; j++) { + faces_grp[j] = faces_arr[groups_array[fg_sta + j]]; + is_calc |= BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); } - /* check sanity (while loop ensures) */ - BLI_assert(f_start != NULL); - - /* make sure the starting face has the correct winding */ - if (dot_v3v3(f_start_cent, f_start->no) < 0.0f) { - BM_face_normal_flip(bm, f_start); - } - - /* 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, f_start); - BMO_elem_flag_enable(bm, f_start, FACE_VIS); - tot_touch++; - - while ((f = STACK_POP(fstack))) { - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_other = l->radial_next; - - if ((l_other == l) || l_other->radial_next != l) { - continue; - } - - if (BMO_elem_flag_test(bm, l_other->f, FACE_FLAG)) { - if (!BMO_elem_flag_test(bm, l_other->f, FACE_VIS)) { - BMO_elem_flag_enable(bm, l_other->f, FACE_VIS); - tot_touch++; - - - if (l_other->v == l->v) { - BM_face_normal_flip(bm, l_other->f); - } - - STACK_PUSH(fstack, l_other->f); - } - } - } + if (is_calc) { + bmo_recalc_face_normals_array(bm, faces_grp, fg_len, FACE_FLAG); } } - MEM_freeN(fstack); + + if (faces_arr) MEM_freeN(faces_arr); + MEM_freeN(faces_grp); + + MEM_freeN(groups_array); + MEM_freeN(group_index); } From 861f9e10f7ea9d1d38130ecbc983fe8f8dac93d0 Mon Sep 17 00:00:00 2001 From: Antony Riakiotakis Date: Mon, 8 Jul 2013 15:35:53 +0000 Subject: [PATCH 15/24] Attempt to fix #35057, disable threading if diameter of the brush becomes too small. Typically this would happen if the number of buckets is clipped to the maximum value. This avoids thread overhead. A better fix might be to do bucket-brush intersection on main thread and dispatch threads to process bucket hits as they become available. This way only one thread at most would end up being used in such cases anyway. A better task scheduler is needed for that though, leaving for after GSOC. --- .../blender/editors/sculpt_paint/paint_image_proj.c | 11 +++++++++++ 1 file changed, 11 insertions(+) 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"); } From 3ce280e825cdd7bcbdd58cb10537a2aa11119ee3 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 17:56:51 +0000 Subject: [PATCH 16/24] Fix #35960, #36044: blender internal viewport rendering crash while editing data. Now the viewport rendering thread will lock the main thread while it is exporting objects to render data. This is not ideal if you have big scenes that might block the UI, but Cycles does the same, and it's fairly quick because the same evaluated mesh can be used as for viewport drawing. It's the only way to get things stable until the thread safe dependency graph is here. This adds a mechanism to the job system for jobs to lock the main thread, using a new 'ticket mutex lock' which is a mutex lock that gives priority to the first thread that tries to lock the mutex. Still to solve: undo/redo crashes. --- source/blender/blenlib/BLI_threads.h | 12 ++++ source/blender/blenlib/intern/threads.c | 46 ++++++++++++++ .../blender/editors/render/render_internal.c | 13 +++- .../render/extern/include/RE_pipeline.h | 1 + .../render/intern/source/convertblender.c | 20 +++--- .../blender/render/intern/source/pipeline.c | 7 ++- source/blender/windowmanager/WM_api.h | 3 + source/blender/windowmanager/intern/wm_jobs.c | 62 ++++++++++++++++--- 8 files changed, 146 insertions(+), 18 deletions(-) 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/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 09138a5523a..03ccb2496a1 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -733,6 +733,7 @@ typedef struct RenderPreview { /* from wmJob */ void *owner; short *stop, *do_update; + wmJob *job; Scene *scene; ScrArea *sa; @@ -913,8 +914,15 @@ 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_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); + // printf("dbase update\n"); } else { @@ -958,7 +966,8 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C, int keep_d 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; 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..bd2d292f633 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -5321,14 +5321,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 +5332,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,7 +5397,6 @@ 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)) @@ -5866,8 +5870,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 dfdfe973241..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) 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) { From 63042da52af05870ab9aacd234efeb75291bd74f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 18:27:32 +0000 Subject: [PATCH 17/24] Fix #36059: region overlap did not show scopes overlapping in the image editor. It's only enabled for some particular regions, this one makes sense to show. --- source/blender/editors/screen/area.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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; } From 751062fc5fea7a17e9a1656391c841a6489194b2 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:26:10 +0000 Subject: [PATCH 18/24] Fix #35979, #35937, #35739: undo crashes and missing updates with blender internal viewport rendering. Lots of tweaks here, mainly: * Stop 3D viewport render and free database before undo. * Accumulate update flags rather than replace them each time it rerenders, to avoid previous updates getting lost. * Don't check against Render struct view parameters for changes, those are set in the job thread which might not run before the next update call. --- source/blender/editors/include/ED_render.h | 2 + source/blender/editors/render/render_intern.h | 2 +- .../blender/editors/render/render_internal.c | 240 +++++++++++------- source/blender/editors/render/render_update.c | 2 +- source/blender/editors/util/undo.c | 13 +- .../blender/render/extern/include/RE_engine.h | 12 +- .../render/intern/source/convertblender.c | 10 +- 7 files changed, 189 insertions(+), 92 deletions(-) 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/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 03ccb2496a1..15aa67feefa 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -728,6 +728,7 @@ 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 */ @@ -744,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) @@ -846,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)) @@ -861,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); @@ -875,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); @@ -901,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 */ @@ -917,12 +911,17 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda /* 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 { @@ -940,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; } } @@ -952,16 +949,88 @@ 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; + + 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); @@ -976,7 +1045,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 */ @@ -991,80 +1059,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); @@ -1114,3 +1135,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/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/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/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index bd2d292f633..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) { @@ -5399,8 +5403,10 @@ void RE_Database_Preprocess(Render *re) 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; From 8ab1fadf3d7a987cbdf378162570e9dc658b74ec Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:41:12 +0000 Subject: [PATCH 19/24] Fix #36063: cycles 3D viewport was incorrectly influenced by blender internal material halo settings. --- source/blender/editors/space_view3d/drawobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); From a2553444fa74cb2a0a43b98bf89b43cbfd1cb103 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 22:41:14 +0000 Subject: [PATCH 20/24] Fix #35969: blender internal and cycles not updating mesh while in edit mode. Patch for blender internal made by Campbell. --- intern/cycles/blender/blender_mesh.cpp | 3 +++ source/blender/editors/render/render_internal.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) 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/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 15aa67feefa..7a2ece66ba4 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -982,8 +982,13 @@ static bool render_view3d_flag_changed(RenderEngine *engine, const bContext *C) if (engine->update_flag & RE_ENGINE_UPDATE_OTHER) job_update_flag |= PR_UPDATE_MATERIAL; - if (engine->update_flag & RE_ENGINE_UPDATE_DATABASE) + 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; From b2b6d564431db71e1c9176ca541a0dd9e7f91168 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 8 Jul 2013 22:57:51 +0000 Subject: [PATCH 21/24] move keymap ui into modules, its not loaded on startup anymore. --- .../bl_ui/space_userpref_keymap.py => modules/rna_keymap_ui.py} | 0 release/scripts/startup/bl_ui/space_userpref.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename release/scripts/{startup/bl_ui/space_userpref_keymap.py => modules/rna_keymap_ui.py} (100%) diff --git a/release/scripts/startup/bl_ui/space_userpref_keymap.py b/release/scripts/modules/rna_keymap_ui.py similarity index 100% rename from release/scripts/startup/bl_ui/space_userpref_keymap.py rename to release/scripts/modules/rna_keymap_ui.py diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index a5e4b6e10bf..3e281e08983 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1052,7 +1052,7 @@ class USERPREF_PT_input(Panel): row.separator() def draw(self, context): - from bl_ui.space_userpref_keymap import draw_keymaps + from rna_keymap_ui import draw_keymaps layout = self.layout From 3d847ed6e6e0a063d1676ad9c01bcc33b23b8ac4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 8 Jul 2013 23:31:45 +0000 Subject: [PATCH 22/24] Fix #36064: cycles direct/indirect light passes with materials that have zero RGB color components gave non-grey results when you might no expect it. What happens is that some of the color channels are zero in the direct light pass because their channel is zero in the color pass. The direct light pass is defined as lighting divided by the color pass, and we can't divide by zero. We do a division after all samples are added together to ensure that multiplication in the compositor gives the exact combined pass even with antialiasing, DoF, .. Found a simple tweak here, instead of setting such channels to zero it will set it to the average of other non-zero color channels, which makes the results look like the expected grey. --- intern/cycles/render/buffers.cpp | 2 +- intern/cycles/util/util_math.h | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) 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) From 6c0d97fbcfaf60d6d932bb96f01e8caf30062c50 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 00:08:18 +0000 Subject: [PATCH 23/24] fix [#35806] Unable to check "Correct UVs" option in "Loop Cut and Slide" (Keymap Editor) --- source/blender/makesrna/intern/rna_access.c | 9 +++++++++ 1 file changed, 9 insertions(+) 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; } From 14ab39c5e0922ad1f7b92abda7c2cc73df03006f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 9 Jul 2013 00:13:17 +0000 Subject: [PATCH 24/24] minor improvements - calc normals only check flag when needed. - keymap, dont get name unless its needed. - keymap, avoid property lookup. - idprop debug print, include pointer, helpful for troubleshooting. --- source/blender/bmesh/intern/bmesh_queries.c | 2 +- source/blender/bmesh/operators/bmo_normals.c | 5 ++++- source/blender/editors/interface/interface_templates.c | 4 ++-- source/blender/python/generic/idprop_py_api.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index c0c4e0f0209..a00bef0228b 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -1761,7 +1761,7 @@ float BM_mesh_calc_volume(BMesh *bm, bool is_signed) /** * TODO (as we need) - * - option to walk over edges. + * - option to walk over faces by verts. * - option to walk over non manifold edges. * * \param bm the BMesh. diff --git a/source/blender/bmesh/operators/bmo_normals.c b/source/blender/bmesh/operators/bmo_normals.c index 7116ef82a32..c4200a37748 100644 --- a/source/blender/bmesh/operators/bmo_normals.c +++ b/source/blender/bmesh/operators/bmo_normals.c @@ -166,7 +166,10 @@ void bmo_recalc_face_normals_exec(BMesh *bm, BMOperator *op) for (j = 0; j < fg_len; j++) { faces_grp[j] = faces_arr[groups_array[fg_sta + j]]; - is_calc |= BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); + + if (is_calc == false) { + is_calc = BMO_elem_flag_test_bool(bm, faces_grp[j], FACE_FLAG); + } } if (is_calc) { 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/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);