Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2020-09-14 21:33:55 +0300
committerHans Goudey <h.goudey@me.com>2020-09-14 21:33:55 +0300
commit18d384ffaf93dbb52b2888fb6f25ae7c712fdf9f (patch)
treed3bc3fe40ccd474129280bb4db5ac75eb3c3c067
parenta67067253b85aba5b3934fd749f589ebabfdc48b (diff)
parent601d323d96f20d00f00218931171c9d128ac0388 (diff)
Merge branch 'property-search-start-end-operators' into property-search-highlight-tabs
-rw-r--r--doc/python_api/rst/info_gotcha.rst26
-rw-r--r--release/scripts/modules/bl_i18n_utils/bl_extract_messages.py21
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py2
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c13
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c12
-rw-r--r--source/blender/blenkernel/intern/layer.c4
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc82
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/blenloader/intern/versioning_290.c29
-rw-r--r--source/blender/blenloader/intern/writefile.c9
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.c13
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon_edgenet.c6
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.cc19
-rw-r--r--source/blender/bmesh/tools/bmesh_boolean.h2
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c18
-rw-r--r--source/blender/draw/engines/image/image_engine.c8
-rw-r--r--source/blender/draw/engines/image/image_private.h4
-rw-r--r--source/blender/draw/engines/image/image_shader.c17
-rw-r--r--source/blender/draw/engines/image/shaders/engine_image_frag.glsl31
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_uv.c15
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl2
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h3
-rw-r--r--source/blender/draw/engines/workbench/workbench_transparent.c23
-rw-r--r--source/blender/draw/intern/DRW_render.h4
-rw-r--r--source/blender/draw/intern/draw_manager_data.c23
-rw-r--r--source/blender/draw/intern/draw_view.c4
-rw-r--r--source/blender/draw/tests/shaders_test.cc3
-rw-r--r--source/blender/editors/interface/interface_layout.c4
-rw-r--r--source/blender/editors/io/io_alembic.c9
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c5
-rw-r--r--source/blender/editors/screen/area.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c2
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c13
-rw-r--r--source/blender/editors/space_file/file_ops.c1
-rw-r--r--source/blender/editors/space_script/script_edit.c35
-rw-r--r--source/blender/editors/transform/transform_constraints.c18
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c70
-rw-r--r--source/blender/gpu/intern/gpu_state.cc2
-rw-r--r--source/blender/imbuf/intern/cache.c36
-rw-r--r--source/blender/imbuf/intern/colormanagement.c99
-rw-r--r--source/blender/imbuf/intern/divers.c30
-rw-r--r--source/blender/imbuf/intern/imageprocess.c37
-rw-r--r--source/blender/imbuf/intern/stereoimbuf.c36
-rw-r--r--source/blender/imbuf/intern/tiff.c62
-rw-r--r--source/blender/io/alembic/ABC_alembic.h1
-rw-r--r--source/blender/io/alembic/CMakeLists.txt2
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc268
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.h96
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.cc41
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_abstract.h41
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_camera.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_curves.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.cc10
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_instance.h2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc8
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.cc11
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_nurbs.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.cc5
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_points.h1
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.cc11
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_transform.h4
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h8
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesdna/intern/makesdna.c6
-rw-r--r--source/blender/makesrna/intern/rna_armature.c12
-rw-r--r--source/blender/makesrna/intern/rna_depsgraph.c19
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c34
-rw-r--r--source/blender/makesrna/intern/rna_object.c17
-rw-r--r--source/blender/makesrna/intern/rna_rna.c9
-rw-r--r--source/blender/makesrna/intern/rna_space.c2
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c12
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.c660
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache_pc2.c2
-rw-r--r--source/blender/python/intern/bpy_rna.c32
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c10
-rw-r--r--tests/python/alembic_export_tests.py64
82 files changed, 1752 insertions, 518 deletions
diff --git a/doc/python_api/rst/info_gotcha.rst b/doc/python_api/rst/info_gotcha.rst
index 0713ffa54a1..eb5cc143a2c 100644
--- a/doc/python_api/rst/info_gotcha.rst
+++ b/doc/python_api/rst/info_gotcha.rst
@@ -677,7 +677,8 @@ Here are some general hints to avoid running into these problems:
Undo/Redo
---------
-Undo invalidates all :class:`bpy.types.ID` instances (Object, Scene, Mesh, Light, etc.).
+For safety, you should assume that undo and redo always invalidates all :class:`bpy.types.ID`
+instances (Object, Scene, Mesh, Light, etc.), as weel obviously as all of their sub-data.
This example shows how you can tell undo changes the memory locations:
@@ -686,7 +687,7 @@ This example shows how you can tell undo changes the memory locations:
>>> hash(bpy.context.object)
-9223372036849950810
-Move the active object, then undo:
+Delete the active object, then undo:
>>> hash(bpy.context.object)
-9223372036849951740
@@ -695,6 +696,16 @@ As suggested above, simply not holding references to data when Blender is used
interactively by the user is the only way to make sure that the script doesn't become unstable.
+.. note::
+
+ Modern undo/redo system does not systematically invalidate all pointers anymore.
+ Some data (in fact, most data, in typical cases), which were detected as unchanged for a
+ particular history step, may remain unchanged and hence their pointers may remain valid.
+
+ Be aware that if you want to take advantage of this behavior for some reason, there is no
+ guarantee of any kind that it will be safe and consistent. Use it at your own risk.
+
+
Undo & Library Data
^^^^^^^^^^^^^^^^^^^
@@ -712,6 +723,17 @@ So it's best to consider modifying library data an advanced usage of the API
and only to use it when you know what you're doing.
+Abusing RNA property callbacks
+------------------------------
+
+Python-defined RNA properties can have custom callbacks. Trying to perform complex operations
+from there, like calling an operator, may work, but is not officialy recommended nor supported.
+
+Main reason is that those callback should be very fast, but additionally, it may for example
+create issues with undo/redo system (most operators store an history step, and editing an RNA
+property does so as well), trigger infinite update loops, and so on.
+
+
Edit-Mode / Memory Access
-------------------------
diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
index cee8f89abd3..b4abf572dbc 100644
--- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
+++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
@@ -456,9 +456,11 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
Recursively get strings, needed in case we have "Blah" + "Blah", passed as an argument in that case it won't
evaluate to a string. However, break on some kind of stopper nodes, like e.g. Subscript.
"""
- if type(node) == ast.Str:
+ # New in py 3.8: all constants are of type 'ast.Constant'.
+ # 'ast.Str' will have to be removed when we officially switch to this version.
+ if type(node) in {ast.Str, getattr(ast, "Constant", None)}:
eval_str = ast.literal_eval(node)
- if eval_str:
+ if eval_str and type(eval_str) == str:
yield (is_split, eval_str, (node,))
else:
is_split = (type(node) in separate_nodes)
@@ -624,6 +626,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
}
for fp in files:
+ # ~ print("Checking File ", fp)
with open(fp, 'r', encoding="utf8") as filedata:
root_node = ast.parse(filedata.read(), fp, 'exec')
@@ -631,8 +634,8 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
for node in ast.walk(root_node):
if type(node) == ast.Call:
- # print("found function at")
- # print("%s:%d" % (fp, node.lineno))
+ # ~ print("found function at")
+ # ~ print("%s:%d" % (fp, node.lineno))
# We can't skip such situations! from blah import foo\nfoo("bar") would also be an ast.Name func!
if type(node.func) == ast.Name:
@@ -657,31 +660,31 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
if kw.arg == arg_kw:
context_elements[arg_kw] = kw.value
break
- # print(context_elements)
+ # ~ print(context_elements)
for kws, proc in translate_kw[msgid]:
if set(kws) <= context_elements.keys():
args = tuple(context_elements[k] for k in kws)
- #print("running ", proc, " with ", args)
+ # ~ print("running ", proc, " with ", args)
ctxt = proc(*args)
if ctxt:
msgctxts[msgid] = ctxt
break
- # print(translate_args)
+ # ~ print(func_args)
# do nothing if not found
for arg_kw, (arg_pos, _) in func_args.items():
msgctxt = msgctxts[arg_kw]
estr_lst = [(None, ())]
if arg_pos < len(node.args):
estr_lst = extract_strings_split(node.args[arg_pos])
- #print(estr, nds)
else:
for kw in node.keywords:
if kw.arg == arg_kw:
+ # ~ print(kw.arg, kw.value)
estr_lst = extract_strings_split(kw.value)
break
- #print(estr, nds)
for estr, nds in estr_lst:
+ # ~ print(estr, nds)
if estr:
if nds:
msgsrc = "{}:{}".format(fp_rel, sorted({nd.lineno for nd in nds})[0])
diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 8a67a598ccd..4e0220ee1cc 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -283,6 +283,8 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
del _initialize
if reload_scripts:
+ _bpy.context.window_manager.tag_script_reload()
+
import gc
print("gc.collect() -> %d" % gc.collect())
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index a9b0eede055..66a7ae757a2 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -185,6 +185,19 @@ BoundBox *BKE_gpencil_boundbox_get(Object *ob)
boundbox_gpencil(ob);
+ Object *ob_orig = (Object *)DEG_get_original_id(&ob->id);
+ /* Update orig object's boundbox with re-computed evaluated values. This function can be
+ * called with the evaluated object and need update the original object bound box data
+ * to keep both values synchronized. */
+ if ((ob_orig != NULL) && (ob != ob_orig)) {
+ if (ob_orig->runtime.bb == NULL) {
+ ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox");
+ }
+ for (int i = 0; i < 8; i++) {
+ copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]);
+ }
+ }
+
return ob->runtime.bb;
}
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index f37e038e69e..8e2e3fd621c 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -272,10 +272,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
* context and might as well ensure we have as much space free as possible. */
gpu_free_unused_buffers();
- /* Free GPU textures when requesting a different render pass/layer. */
- if (ima->gpu_pass != iuser->pass || ima->gpu_layer != iuser->layer) {
- ima->gpu_pass = iuser->pass;
- ima->gpu_layer = iuser->layer;
+ /* Free GPU textures when requesting a different render pass/layer.
+ * When `iuser` isn't set (texture painting single image mode) we assume that
+ * the current `pass` and `layer` should be 0. */
+ short requested_pass = iuser ? iuser->pass : 0;
+ short requested_layer = iuser ? iuser->layer : 0;
+ if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer) {
+ ima->gpu_pass = requested_pass;
+ ima->gpu_layer = requested_layer;
ima->gpuflag |= IMA_GPU_REFRESH;
}
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 1ad34fde0fa..dd8a22e10da 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1111,8 +1111,8 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o
return false;
}
- /* If not using local view or local collection the object may still be in a hidden collection. */
- if (((v3d->localvd) == NULL) && ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0)) {
+ /* If not using local collection the object may still be in a hidden collection. */
+ if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) {
return (ob->base_flag & BASE_VISIBLE_VIEWLAYER) != 0;
}
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index e92751efe72..a6ab30a3b26 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -2068,11 +2068,11 @@ static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding)
return true;
}
for (int i = 1; i < nw; ++i) {
- if (winding[i] == 0) {
- return true;
+ if (winding[i] == 1) {
+ return false;
}
}
- return false;
+ return true;
}
default:
return false;
@@ -2398,8 +2398,7 @@ static IMesh gwn_boolean(const IMesh &tm,
IMesh ans;
Vector<Face *> out_faces;
out_faces.reserve(tm.face_size());
- BLI_assert(nshapes == 2); /* TODO: generalize. */
- UNUSED_VARS_NDEBUG(nshapes);
+ Array<int> winding(nshapes, 0);
for (int p : pinfo.index_range()) {
const Patch &patch = pinfo.patch(p);
/* For test triangle, choose one in the middle of patch list
@@ -2421,40 +2420,41 @@ static IMesh gwn_boolean(const IMesh &tm,
if (dbg_level > 0) {
std::cout << "test point = " << test_point_db << "\n";
}
- int other_shape = 1 - shape;
- /* The point_is_inside_shape function has to approximate if the other
- * shape is not PWN. For most operations, even a hint of being inside
- * gives good results, but when shape is the cutter in a Difference
- * operation, we want to be pretty sure that the point is inside other_shape.
- * E.g., T75827.
- */
- bool need_high_confidence = (op == BoolOpType::Difference) && (shape == 1);
- bool inside = point_is_inside_shape(
- tm, shape_fn, test_point_db, other_shape, need_high_confidence);
- if (dbg_level > 0) {
- std::cout << "test point is " << (inside ? "inside\n" : "outside\n");
- }
- bool do_remove;
- bool do_flip;
- switch (op) {
- case BoolOpType::Intersect:
- do_remove = !inside;
- do_flip = false;
- break;
- case BoolOpType::Union:
- do_remove = inside;
- do_flip = false;
- break;
- case BoolOpType::Difference:
- do_remove = (shape == 0) ? inside : !inside;
- do_flip = (shape == 1);
- break;
- default:
- do_remove = false;
- do_flip = false;
- BLI_assert(false);
+ for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
+ if (other_shape == shape) {
+ continue;
+ }
+ /* The point_is_inside_shape function has to approximate if the other
+ * shape is not PWN. For most operations, even a hint of being inside
+ * gives good results, but when shape is a cutter in a Difference
+ * operation, we want to be pretty sure that the point is inside other_shape.
+ * E.g., T75827.
+ */
+ bool need_high_confidence = (op == BoolOpType::Difference) && (shape != 0);
+ bool inside = point_is_inside_shape(
+ tm, shape_fn, test_point_db, other_shape, need_high_confidence);
+ if (dbg_level > 0) {
+ std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
+ << other_shape << "\n";
+ }
+ winding[other_shape] = inside;
}
+ /* Find out the "in the output volume" flag for each of the cases of winding[shape] == 0
+ * and winding[shape] == 1. If the flags are different, this patch should be in the output.
+ * Also, if this is a Difference and the shape isn't the first one, need to flip the normals.
+ */
+ winding[shape] = 0;
+ bool in_output_volume_0 = apply_bool_op(op, winding);
+ winding[shape] = 1;
+ bool in_output_volume_1 = apply_bool_op(op, winding);
+ bool do_remove = in_output_volume_0 == in_output_volume_1;
+ bool do_flip = !do_remove && op == BoolOpType::Difference && shape != 0;
if (dbg_level > 0) {
+ std::cout << "winding = ";
+ for (int i = 0; i < nshapes; ++i) {
+ std::cout << winding[i] << " ";
+ }
+ std::cout << "\niv0=" << in_output_volume_0 << ", iv1=" << in_output_volume_1 << "\n";
std::cout << "result for patch " << p << ": remove=" << do_remove << ", flip=" << do_flip
<< "\n";
}
@@ -3270,13 +3270,7 @@ IMesh boolean_trimesh(IMesh &tm_in,
if (tm_in.face_size() == 0) {
return IMesh(tm_in);
}
- IMesh tm_si;
- if (use_self) {
- tm_si = trimesh_self_intersect(tm_in, arena);
- }
- else {
- tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena);
- }
+ IMesh tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena);
if (dbg_level > 1) {
write_obj_mesh(tm_si, "boolean_tm_si");
std::cout << "\nboolean_tm_input after intersection:\n" << tm_si;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index d013006b602..409a465965f 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -6557,6 +6557,9 @@ static void read_libblock_undo_restore_identical(
if (ob->proxy != NULL) {
ob->proxy->proxy_from = ob;
}
+ /* For undo we stay in object mode during undo presses, so keep editmode disabled for re-used
+ * data-blocks too. */
+ ob->mode &= ~OB_MODE_EDIT;
}
}
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index cf07e9acad3..7805dbdcefa 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -482,7 +482,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Initialise additional velocity parameter for CacheFiles. */
+ /* Initialize additional velocity parameter for #CacheFile's. */
if (!DNA_struct_elem_find(
fd->filesdna, "MeshSeqCacheModifierData", "float", "velocity_scale")) {
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
@@ -526,19 +526,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
-
- /* Initialize solver for Boolean. */
- if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "enum", "solver")) {
- for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Boolean) {
- BooleanModifierData *bmd = (BooleanModifierData *)md;
- bmd->solver = eBooleanModifierSolver_Fast;
- bmd->flag = 0;
- }
- }
- }
- }
}
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
@@ -597,6 +584,20 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Solver and Collections for Boolean. */
+ if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "char", "solver")) {
+ for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Boolean) {
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ bmd->solver = eBooleanModifierSolver_Fast;
+ bmd->flag = eBooleanModifierFlag_Object;
+ }
+ }
+ }
+ }
+
/* Keep this block, even when empty. */
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index b3e937a29b2..183ed3668b7 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1303,10 +1303,17 @@ static void write_shaderfxs(BlendWriter *writer, ListBase *fxbase)
static void write_object(BlendWriter *writer, Object *ob, const void *id_address)
{
- if (ob->id.us > 0 || BLO_write_is_undo(writer)) {
+ const bool is_undo = BLO_write_is_undo(writer);
+ if (ob->id.us > 0 || is_undo) {
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
BKE_object_runtime_reset(ob);
+ if (is_undo) {
+ /* For undo we stay in object mode during undo presses, so keep editmode disabled on save as
+ * well, can help reducing false detection of changed datablocks. */
+ ob->mode &= ~OB_MODE_EDIT;
+ }
+
/* write LibData */
BLO_write_id_struct(writer, Object, id_address, &ob->id);
BKE_id_blend_write(writer, &ob->id);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c
index 4671df90d53..2269837cc8e 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c
@@ -893,19 +893,14 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
- const bool apply_offset = (ofs && (currkey != actkey) &&
- (bm->shapenr - 1 == currkey->relative));
- int cd_shape_offset;
int keyi;
const float(*ofs_pt)[3] = ofs;
float *newkey, (*oldkey)[3], *fp;
j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
- cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j);
- if (cd_shape_offset < 0) {
- /* The target Mesh has more shapekeys than the BMesh. */
- continue;
- }
+ const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j);
+ const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) &&
+ (bm->shapenr - 1 == currkey->relative);
fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
oldkey = currkey->data;
@@ -927,7 +922,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
}
- else if (j != -1) {
+ else if (cd_shape_offset != -1) {
/* In most cases this runs. */
copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
}
diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
index 39395cb9222..b92e431c86c 100644
--- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
+++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c
@@ -494,7 +494,7 @@ bool BM_face_split_edgenet(BMesh *bm,
}
/* These arrays used to be stack memory, however they can be
- * large for single faces with complex edgenets, see: T65980. */
+ * large for single faces with complex edge-nets, see: T65980. */
/* over-alloc (probably 2-4 is only used in most cases), for the biggest-fan */
edge_order = MEM_mallocN(sizeof(*edge_order) * edge_order_len, __func__);
@@ -1650,8 +1650,8 @@ finally:
{
struct TempVertPair *tvp = temp_vert_pairs.list;
do {
- /* we must _never_ create connections here
- * (inface the islands can't have a connection at all) */
+ /* We must _never_ create connections here
+ * (in case the islands can't have a connection at all). */
BLI_assert(BM_edge_exists(tvp->v_orig, tvp->v_temp) == NULL);
} while ((tvp = tvp->next));
}
diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc
index d2f73dd63ec..56585cb722e 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.cc
+++ b/source/blender/bmesh/tools/bmesh_boolean.cc
@@ -194,6 +194,7 @@ static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out)
/* Initially mark all existing faces as "don't keep", except hidden faces.
* Also, save current #BMFace pointers as creating faces will disturb the table. */
Array<BMFace *> old_bmfs(bm->totface);
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
for (int f = 0; f < bm->totface; ++f) {
BMFace *bmf = BM_face_at_index(bm, f);
old_bmfs[f] = bmf;
@@ -331,6 +332,7 @@ static bool bmesh_boolean(BMesh *bm,
const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
+ int nshapes,
const bool use_self,
const bool use_separate_all,
const BoolOpType boolean_mode)
@@ -339,10 +341,9 @@ static bool bmesh_boolean(BMesh *bm,
IMesh m_triangulated;
IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena);
std::function<int(int)> shape_fn;
- int nshapes;
if (use_self && boolean_mode == BoolOpType::None) {
/* Unary knife operation. Want every face where test_fn doesn't return -1. */
- nshapes = 1;
+ BLI_assert(nshapes == 1);
shape_fn = [bm, test_fn, user_data](int f) {
BMFace *bmf = BM_face_at_index(bm, f);
if (test_fn(bmf, user_data) != -1) {
@@ -352,15 +353,11 @@ static bool bmesh_boolean(BMesh *bm,
};
}
else {
- nshapes = 2;
shape_fn = [bm, test_fn, user_data](int f) {
BMFace *bmf = BM_face_at_index(bm, f);
int test_val = test_fn(bmf, user_data);
- if (test_val == 0) {
- return 0;
- }
- if (test_val == 1) {
- return 1;
+ if (test_val >= 0) {
+ return test_val;
}
return -1;
};
@@ -403,6 +400,7 @@ bool BM_mesh_boolean(BMesh *bm,
const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
+ const int nshapes,
const bool use_self,
const int boolean_mode)
{
@@ -412,6 +410,7 @@ bool BM_mesh_boolean(BMesh *bm,
looptris_tot,
test_fn,
user_data,
+ nshapes,
use_self,
false,
static_cast<blender::meshintersect::BoolOpType>(boolean_mode));
@@ -430,6 +429,7 @@ bool BM_mesh_boolean_knife(BMesh *bm,
const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
+ const int nshapes,
const bool use_self,
const bool use_separate_all)
{
@@ -438,6 +438,7 @@ bool BM_mesh_boolean_knife(BMesh *bm,
looptris_tot,
test_fn,
user_data,
+ nshapes,
use_self,
use_separate_all,
blender::meshintersect::BoolOpType::None);
@@ -448,6 +449,7 @@ bool BM_mesh_boolean(BMesh *UNUSED(bm),
const int UNUSED(looptris_tot),
int (*test_fn)(BMFace *, void *),
void *UNUSED(user_data),
+ const int UNUSED(nshapes),
const bool UNUSED(use_self),
const int UNUSED(boolean_mode))
{
@@ -468,6 +470,7 @@ bool BM_mesh_boolean_knife(BMesh *UNUSED(bm),
const int UNUSED(looptris_tot),
int (*test_fn)(BMFace *, void *),
void *UNUSED(user_data),
+ const int UNUSED(nshapes),
const bool UNUSED(use_self),
const bool UNUSED(use_separate_all))
{
diff --git a/source/blender/bmesh/tools/bmesh_boolean.h b/source/blender/bmesh/tools/bmesh_boolean.h
index d1b4fb2509c..04b5205ec84 100644
--- a/source/blender/bmesh/tools/bmesh_boolean.h
+++ b/source/blender/bmesh/tools/bmesh_boolean.h
@@ -29,6 +29,7 @@ bool BM_mesh_boolean(BMesh *bm,
const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
+ const int nshapes,
const bool use_self,
const int boolean_mode);
@@ -37,6 +38,7 @@ bool BM_mesh_boolean_knife(BMesh *bm,
const int looptris_tot,
int (*test_fn)(BMFace *f, void *user_data),
void *user_data,
+ const int nshapes,
const bool use_self,
const bool use_separate_all);
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index d4b1d421603..58897272425 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -647,7 +647,8 @@ GPUShader *EEVEE_shaders_effect_motion_blur_velocity_tiles_expand_sh_get(void)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Ambient Occlusion */
+/** \name Ambient Occlusion
+ * \{ */
GPUShader *EEVEE_shaders_effect_ambient_occlusion_sh_get(void)
{
@@ -679,7 +680,8 @@ GPUShader *EEVEE_shaders_effect_ambient_occlusion_debug_sh_get(void)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Render Passes */
+/** \name Render Passes
+ * \{ */
GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void)
{
@@ -693,7 +695,8 @@ GPUShader *EEVEE_shaders_renderpasses_post_process_sh_get(void)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Screen Raytrace */
+/** \name Screen Raytrace
+ * \{ */
struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOptions options)
{
@@ -730,7 +733,8 @@ struct GPUShader *EEVEE_shaders_effect_screen_raytrace_sh_get(EEVEE_SSRShaderOpt
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Shadows */
+/** \name Shadows
+ * \{ */
struct GPUShader *EEVEE_shaders_shadow_sh_get()
{
@@ -753,7 +757,8 @@ struct GPUShader *EEVEE_shaders_shadow_accum_sh_get()
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Subsurface */
+/** \name Subsurface
+ * \{ */
struct GPUShader *EEVEE_shaders_subsurface_first_pass_sh_get()
{
@@ -786,7 +791,8 @@ struct GPUShader *EEVEE_shaders_subsurface_translucency_sh_get()
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Volumes */
+/** \name Volumes
+ * \{ */
struct GPUShader *EEVEE_shaders_volumes_clear_sh_get()
{
diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c
index 9f1278b473b..90bfb38dadf 100644
--- a/source/blender/draw/engines/image/image_engine.c
+++ b/source/blender/draw/engines/image/image_engine.c
@@ -42,8 +42,7 @@
#define SIMA_DRAW_FLAG_APPLY_ALPHA (1 << 1)
#define SIMA_DRAW_FLAG_SHUFFLING (1 << 2)
#define SIMA_DRAW_FLAG_DEPTH (1 << 3)
-#define SIMA_DRAW_FLAG_TILED (1 << 4)
-#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 5)
+#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 4)
static void image_cache_image_add(DRWShadingGroup *grp, Image *image)
{
@@ -179,10 +178,9 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser
draw_flags |= SIMA_DRAW_FLAG_APPLY_ALPHA;
}
- GPUShader *shader = IMAGE_shader_image_get();
+ GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture);
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass);
- if (tex_tile_data != NULL) {
- draw_flags |= SIMA_DRAW_FLAG_TILED;
+ if (is_tiled_texture) {
DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, state);
DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data);
}
diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.h
index d11d868d4d2..312a05e0b3b 100644
--- a/source/blender/draw/engines/image/image_private.h
+++ b/source/blender/draw/engines/image/image_private.h
@@ -34,7 +34,7 @@ struct GPUTexture;
/* *********** LISTS *********** */
/* GPUViewport.storage
- * Is freed everytime the viewport engine changes */
+ * Is freed every time the viewport engine changes. */
typedef struct IMAGE_PassList {
DRWPass *image_pass;
} IMAGE_PassList;
@@ -60,7 +60,7 @@ typedef struct IMAGE_Data {
} IMAGE_Data;
/* image_shader.c */
-GPUShader *IMAGE_shader_image_get(void);
+GPUShader *IMAGE_shader_image_get(bool is_tiled_image);
void IMAGE_shader_library_ensure(void);
void IMAGE_shader_free(void);
diff --git a/source/blender/draw/engines/image/image_shader.c b/source/blender/draw/engines/image/image_shader.c
index 433c79e20cf..0e0c432c32f 100644
--- a/source/blender/draw/engines/image/image_shader.c
+++ b/source/blender/draw/engines/image/image_shader.c
@@ -37,7 +37,7 @@ extern char datatoc_engine_image_frag_glsl[];
extern char datatoc_engine_image_vert_glsl[];
typedef struct IMAGE_Shaders {
- GPUShader *image_sh;
+ GPUShader *image_sh[2];
} IMAGE_Shaders;
static struct {
@@ -56,14 +56,19 @@ void IMAGE_shader_library_ensure(void)
}
}
-GPUShader *IMAGE_shader_image_get(void)
+GPUShader *IMAGE_shader_image_get(bool is_tiled_image)
{
+ const int index = is_tiled_image ? 1 : 0;
IMAGE_Shaders *sh_data = &e_data.shaders;
- if (!sh_data->image_sh) {
- sh_data->image_sh = DRW_shader_create_with_shaderlib(
- datatoc_engine_image_vert_glsl, NULL, datatoc_engine_image_frag_glsl, e_data.lib, NULL);
+ if (!sh_data->image_sh[index]) {
+ sh_data->image_sh[index] = DRW_shader_create_with_shaderlib(
+ datatoc_engine_image_vert_glsl,
+ NULL,
+ datatoc_engine_image_frag_glsl,
+ e_data.lib,
+ is_tiled_image ? "#define TILED_IMAGE\n" : NULL);
}
- return sh_data->image_sh;
+ return sh_data->image_sh[index];
}
void IMAGE_shader_free(void)
diff --git a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl
index 5c5d9362dfc..a79f4915c4d 100644
--- a/source/blender/draw/engines/image/shaders/engine_image_frag.glsl
+++ b/source/blender/draw/engines/image/shaders/engine_image_frag.glsl
@@ -5,12 +5,14 @@
#define SIMA_DRAW_FLAG_APPLY_ALPHA (1 << 1)
#define SIMA_DRAW_FLAG_SHUFFLING (1 << 2)
#define SIMA_DRAW_FLAG_DEPTH (1 << 3)
-#define SIMA_DRAW_FLAG_TILED (1 << 4)
-#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 5)
+#define SIMA_DRAW_FLAG_DO_REPEAT (1 << 4)
+#ifdef TILED_IMAGE
uniform sampler2DArray imageTileArray;
uniform sampler1DArray imageTileData;
+#else
uniform sampler2D imageTexture;
+#endif
uniform bool imgPremultiplied;
uniform int drawFlags;
@@ -25,6 +27,7 @@ in vec2 uvs;
out vec4 fragColor;
+#ifdef TILED_IMAGE
/* TODO(fclem) deduplicate code. */
bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
{
@@ -50,26 +53,26 @@ bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
return true;
}
+#endif
void main()
{
vec4 tex_color;
/* Read texture */
- if ((drawFlags & SIMA_DRAW_FLAG_TILED) != 0) {
- vec3 co = vec3(uvs, 0.0);
- if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) {
- tex_color = texture(imageTileArray, co);
- }
- else {
- tex_color = vec4(1.0, 0.0, 1.0, 1.0);
- }
+#ifdef TILED_IMAGE
+ vec3 co = vec3(uvs, 0.0);
+ if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) {
+ tex_color = texture(imageTileArray, co);
}
else {
- vec2 uvs_clamped = ((drawFlags & SIMA_DRAW_FLAG_DO_REPEAT) != 0) ?
- fract(uvs) :
- clamp(uvs, vec2(0.0), vec2(1.0));
- tex_color = texture(imageTexture, uvs_clamped);
+ tex_color = vec4(1.0, 0.0, 1.0, 1.0);
}
+#else
+ vec2 uvs_clamped = ((drawFlags & SIMA_DRAW_FLAG_DO_REPEAT) != 0) ?
+ fract(uvs) :
+ clamp(uvs, vec2(0.0), vec2(1.0));
+ tex_color = texture(imageTexture, uvs_clamped);
+#endif
if ((drawFlags & SIMA_DRAW_FLAG_APPLY_ALPHA) != 0) {
if (!imgPremultiplied && tex_color.a != 0.0 && tex_color.a != 1.0) {
diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c
index 109db6433e0..adbf7168394 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_uv.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c
@@ -96,16 +96,19 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata)
(ts->uv_selectmode == UV_SELECT_FACE);
const bool do_uvstretching_overlay = is_image_type && is_uv_editor && is_edit_mode &&
((sima->flag & SI_DRAW_STRETCH) != 0);
+ const bool do_tex_paint_shadows = (sima->flag & SI_NO_DRAW_TEXPAINT) == 0;
+
pd->edit_uv.do_faces = do_faces && !do_uvstretching_overlay;
pd->edit_uv.do_face_dots = do_faces && do_face_dots;
pd->edit_uv.do_uv_overlay = do_uv_overlay;
- pd->edit_uv.do_uv_shadow_overlay =
- is_image_type &&
- ((is_paint_mode &&
- ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) ||
- (is_view_mode && ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) ||
- (do_uv_overlay && (show_modified_uvs)));
+ pd->edit_uv.do_uv_shadow_overlay = is_image_type &&
+ ((is_paint_mode && do_tex_paint_shadows &&
+ ((draw_ctx->object_mode &
+ (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) ||
+ (is_view_mode && do_tex_paint_shadows &&
+ ((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) ||
+ (do_uv_overlay && (show_modified_uvs)));
pd->edit_uv.do_uv_stretching_overlay = do_uvstretching_overlay;
pd->edit_uv.uv_opacity = sima->uv_opacity;
pd->edit_uv.do_tiled_image_overlay = is_image_type && is_tiled_image;
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
index b77e168889f..856654549ca 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_merge_infront_frag.glsl
@@ -8,6 +8,8 @@ out vec4 fragColor;
void main()
{
float depth = texture(depthBuffer, uvcoordsvar.st).r;
+ /* Fix issues with Intel drivers (see T80023). */
+ fragColor = vec4(0.0);
/* Discard background pixels. */
if (depth == 1.0) {
discard;
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 8983826f16f..d377f09ac73 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -131,6 +131,9 @@ typedef struct WORKBENCH_PassList {
struct DRWPass *transp_accum_ps;
struct DRWPass *transp_accum_infront_ps;
+ struct DRWPass *transp_depth_infront_ps;
+ struct DRWPass *transp_depth_ps;
+
struct DRWPass *shadow_ps[2];
struct DRWPass *merge_infront_ps;
diff --git a/source/blender/draw/engines/workbench/workbench_transparent.c b/source/blender/draw/engines/workbench/workbench_transparent.c
index 1c8575ddc12..94fcd8b5a9d 100644
--- a/source/blender/draw/engines/workbench/workbench_transparent.c
+++ b/source/blender/draw/engines/workbench/workbench_transparent.c
@@ -91,16 +91,18 @@ void workbench_transparent_cache_init(WORKBENCH_Data *vedata)
{
int transp = 1;
for (int infront = 0; infront < 2; infront++) {
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT;
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_OIT |
+ wpd->cull_state | wpd->clip_state;
DRWPass *pass;
if (infront) {
- DRW_PASS_CREATE(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state);
- pass = psl->transp_accum_infront_ps;
+ psl->transp_accum_infront_ps = pass = DRW_pass_create("transp_accum_infront", state);
+ DRW_PASS_INSTANCE_CREATE(
+ psl->transp_depth_infront_ps, pass, state | DRW_STATE_WRITE_DEPTH);
}
else {
- DRW_PASS_CREATE(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state);
- pass = psl->transp_accum_ps;
+ psl->transp_accum_ps = pass = DRW_pass_create("transp_accum", state);
+ DRW_PASS_INSTANCE_CREATE(psl->transp_depth_ps, pass, state | DRW_STATE_WRITE_DEPTH);
}
for (eWORKBENCH_DataType data = 0; data < WORKBENCH_DATATYPE_MAX; data++) {
@@ -159,20 +161,17 @@ void workbench_transparent_draw_depth_pass(WORKBENCH_Data *data)
const bool do_transparent_depth_pass = psl->outline_ps || wpd->dof_enabled || do_xray_depth_pass;
if (do_transparent_depth_pass) {
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
- if (!DRW_pass_is_empty(psl->transp_accum_ps)) {
+ if (!DRW_pass_is_empty(psl->transp_depth_ps)) {
GPU_framebuffer_bind(fbl->opaque_fb);
/* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */
- DRW_pass_state_set(psl->transp_accum_ps, state | wpd->cull_state | wpd->clip_state);
- DRW_draw_pass(psl->transp_accum_ps);
+ DRW_draw_pass(psl->transp_depth_ps);
}
- if (!DRW_pass_is_empty(psl->transp_accum_infront_ps)) {
+ if (!DRW_pass_is_empty(psl->transp_depth_infront_ps)) {
GPU_framebuffer_bind(fbl->opaque_infront_fb);
/* TODO(fclem) Disable writing to first two buffers. Unnecessary waste of bandwidth. */
- DRW_pass_state_set(psl->transp_accum_infront_ps, state | wpd->cull_state | wpd->clip_state);
- DRW_draw_pass(psl->transp_accum_infront_ps);
+ DRW_draw_pass(psl->transp_depth_infront_ps);
}
}
}
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 30c6f0ad4dc..2744c55a231 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -581,10 +581,6 @@ bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup);
DRWPass *DRW_pass_create(const char *name, DRWState state);
DRWPass *DRW_pass_create_instance(const char *name, DRWPass *original, DRWState state);
void DRW_pass_link(DRWPass *first, DRWPass *second);
-/* TODO Replace with passes inheritance. */
-void DRW_pass_state_set(DRWPass *pass, DRWState state);
-void DRW_pass_state_add(DRWPass *pass, DRWState state);
-void DRW_pass_state_remove(DRWPass *pass, DRWState state);
void DRW_pass_foreach_shgroup(DRWPass *pass,
void (*callback)(void *userData, DRWShadingGroup *shgroup),
void *userData);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 81842f5d2ec..7fe3bc0f071 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -1947,7 +1947,7 @@ DRWPass *DRW_pass_create(const char *name, DRWState state)
{
DRWPass *pass = BLI_memblock_alloc(DST.vmempool->passes);
pass->state = state | DRW_STATE_PROGRAM_POINT_SIZE;
- if (((G.debug_value > 20) && (G.debug_value < 30)) || (G.debug & G_DEBUG)) {
+ if (G.debug & G_DEBUG_GPU) {
BLI_strncpy(pass->name, name, MAX_PASS_NAME);
}
@@ -1962,6 +1962,8 @@ DRWPass *DRW_pass_create(const char *name, DRWState state)
return pass;
}
+/* Create an instance of the original pass that will execute the same drawcalls but with its own
+ * DRWState. */
DRWPass *DRW_pass_create_instance(const char *name, DRWPass *original, DRWState state)
{
DRWPass *pass = DRW_pass_create(name, state);
@@ -1980,6 +1982,10 @@ void DRW_pass_link(DRWPass *first, DRWPass *second)
bool DRW_pass_is_empty(DRWPass *pass)
{
+ if (pass->original) {
+ return DRW_pass_is_empty(pass->original);
+ }
+
LISTBASE_FOREACH (DRWShadingGroup *, shgroup, &pass->shgroups) {
if (!DRW_shgroup_is_empty(shgroup)) {
return false;
@@ -1988,21 +1994,6 @@ bool DRW_pass_is_empty(DRWPass *pass)
return true;
}
-void DRW_pass_state_set(DRWPass *pass, DRWState state)
-{
- pass->state = state | DRW_STATE_PROGRAM_POINT_SIZE;
-}
-
-void DRW_pass_state_add(DRWPass *pass, DRWState state)
-{
- pass->state |= state;
-}
-
-void DRW_pass_state_remove(DRWPass *pass, DRWState state)
-{
- pass->state &= ~state;
-}
-
void DRW_pass_foreach_shgroup(DRWPass *pass,
void (*callback)(void *userData, DRWShadingGroup *shgrp),
void *userData)
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index 3033cf70b29..86f19c6cfd7 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -231,7 +231,7 @@ void DRW_draw_cursor_2d(void)
/* Draw nice Anti Aliased cursor. */
GPU_line_width(1.0f);
- GPU_blend(true);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
/* Draw lines */
@@ -248,7 +248,7 @@ void DRW_draw_cursor_2d(void)
GPU_batch_draw(cursor_batch);
- GPU_blend(false);
+ GPU_blend(GPU_BLEND_NONE);
GPU_line_smooth(false);
GPU_matrix_pop();
GPU_matrix_projection_set(original_proj);
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 8feccc9588e..f99fa04ce75 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -156,7 +156,8 @@ TEST_F(DrawTest, image_glsl_shaders)
{
IMAGE_shader_library_ensure();
- EXPECT_NE(IMAGE_shader_image_get(), nullptr);
+ EXPECT_NE(IMAGE_shader_image_get(false), nullptr);
+ EXPECT_NE(IMAGE_shader_image_get(true), nullptr);
IMAGE_shader_free();
}
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index b528f9e29b2..794cb53d5da 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -5165,6 +5165,10 @@ static bool block_search_panel_label_matches(const uiBlock *block)
return false;
}
+/**
+ * Buttons for search only layouts (closed panel subpanels) have still been added from the
+ * layout functions, but they need to be hidden. Theoretically they could be removed too.
+ */
static void layout_free_and_hide_buttons(uiLayout *layout)
{
LISTBASE_FOREACH_MUTABLE (uiItem *, item, &layout->items) {
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 0c4064b5693..292d8e6066c 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -133,6 +133,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op)
.use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"),
.export_hair = RNA_boolean_get(op->ptr, "export_hair"),
.export_particles = RNA_boolean_get(op->ptr, "export_particles"),
+ .export_custom_properties = RNA_boolean_get(op->ptr, "export_custom_properties"),
.use_instancing = RNA_boolean_get(op->ptr, "use_instancing"),
.packuv = RNA_boolean_get(op->ptr, "packuv"),
.triangulate = RNA_boolean_get(op->ptr, "triangulate"),
@@ -191,6 +192,8 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "flatten", 0, NULL, ICON_NONE);
uiItemR(sub, imfptr, "use_instancing", 0, IFACE_("Use Instancing"), ICON_NONE);
+ uiItemR(sub, imfptr, "export_custom_properties", 0, IFACE_("Custom Properties"), ICON_NONE);
+
sub = uiLayoutColumnWithHeading(col, true, IFACE_("Only"));
uiItemR(sub, imfptr, "selected", 0, IFACE_("Selected Objects"), ICON_NONE);
uiItemR(sub, imfptr, "renderable_only", 0, IFACE_("Renderable Objects"), ICON_NONE);
@@ -449,6 +452,12 @@ void WM_OT_alembic_export(wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "export_particles", 1, "Export Particles", "Exports non-hair particle systems");
+ RNA_def_boolean(ot->srna,
+ "export_custom_properties",
+ true,
+ "Export Custom Properties",
+ "Export custom properties to Alembic .userProperties");
+
RNA_def_boolean(
ot->srna,
"as_background_job",
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 1b5e374b2a7..528ad57b9bf 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -204,8 +204,9 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
}
if (exact) {
+ int nshapes = use_self ? 1 : 2;
has_isect = BM_mesh_boolean_knife(
- em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, use_separate_all);
+ em->bm, em->looptris, em->tottri, test_fn, NULL, nshapes, use_self, use_separate_all);
}
else {
has_isect = BM_mesh_intersect(em->bm,
@@ -373,7 +374,7 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
if (use_exact) {
has_isect = BM_mesh_boolean(
- em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, boolean_operation);
+ em->bm, em->looptris, em->tottri, test_fn, NULL, 2, use_self, boolean_operation);
}
else {
has_isect = BM_mesh_intersect(em->bm,
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 171d6105547..81150d78c9e 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -748,7 +748,7 @@ const char *ED_area_search_filter_get(const ScrArea *area, const ARegion *region
if (area->spacetype == SPACE_PROPERTIES) {
SpaceProperties *sbuts = area->spacedata.first;
if (region->regiontype == RGN_TYPE_WINDOW) {
- return sbuts->runtime.search_string;
+ return sbuts->runtime->search_string;
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 70f8ef89e9a..c9fdf3c238d 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1022,7 +1022,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
break;
}
- BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, false, boolean_mode);
+ BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, false, boolean_mode);
Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, sculpt_mesh);
BM_mesh_free(bm);
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 6ac3201c5f1..147c3175d33 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -109,21 +109,30 @@ static void buttons_free(SpaceLink *sl)
BLI_freelistN(&ct->users);
MEM_freeN(ct);
}
+
+ BLI_assert(sbuts->runtime != NULL);
+ MEM_freeN(sbuts->runtime);
}
/* spacetype; init callback */
-static void buttons_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area))
+static void buttons_init(struct wmWindowManager *UNUSED(wm), ScrArea *area)
{
+ SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first;
+
+ sbuts->runtime = MEM_mallocN(sizeof(SpaceProperties_Runtime), __func__);
+ sbuts->runtime->search_string[0] = '\0';
}
static SpaceLink *buttons_duplicate(SpaceLink *sl)
{
+ SpaceProperties *sfile_old = (SpaceProperties *)sl;
SpaceProperties *sbutsn = MEM_dupallocN(sl);
/* clear or remove stuff from old */
sbutsn->path = NULL;
sbutsn->texuser = NULL;
- sbutsn->runtime.search_string[0] = '\0';
+ sbutsn->runtime = MEM_dupallocN(sfile_old->runtime);
+ sbutsn->runtime->search_string[0] = '\0';
return (SpaceLink *)sbutsn;
}
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index b3587fc7f97..b3f9e8b6ef6 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -1593,7 +1593,6 @@ void file_draw_check_ex(bContext *C, ScrArea *area)
void file_draw_check(bContext *C)
{
- SpaceFile *sfile = CTX_wm_space_file(C);
ScrArea *area = CTX_wm_area(C);
file_draw_check_ex(C, area);
}
diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c
index f739bfe367e..56f99d31cf1 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -115,15 +115,32 @@ static int script_reload_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- WM_script_tag_reload();
-
- /* TODO, this crashes on netrender and keying sets, need to look into why
- * disable for now unless running in debug mode */
- WM_cursor_wait(1);
- BPY_run_string_eval(
- C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)");
- WM_cursor_wait(0);
- WM_event_add_notifier(C, NC_WINDOW, NULL);
+ /* TODO(campbell): this crashes on netrender and keying sets, need to look into why
+ * disable for now unless running in debug mode. */
+
+ /* It would be nice if we could detect when this is called from the Python
+ * only postponing in that case, for now always do it. */
+ if (true) {
+ /* Postpone when called from Python so this can be called from an operator
+ * that might be re-registered, crashing Blender when we try to read from the
+ * freed operator type which, see T80694. */
+ BPY_run_string_exec(C,
+ (const char *[]){"bpy", NULL},
+ "def fn():\n"
+ " bpy.utils.load_scripts(reload_scripts=True)\n"
+ " return None\n"
+ "bpy.app.timers.register(fn)");
+ }
+ else {
+ WM_cursor_wait(true);
+ BPY_run_string_eval(
+ C, (const char *[]){"bpy", NULL}, "bpy.utils.load_scripts(reload_scripts=True)");
+ WM_cursor_wait(false);
+ }
+
+ /* Note that #WM_script_tag_reload is called from `bpy.utils.load_scripts`,
+ * any additional updates required by this operator should go there. */
+
return OPERATOR_FINISHED;
#else
UNUSED_VARS(C, op);
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 0eaae7f17cd..68bde912fe5 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -100,24 +100,6 @@ static void constraint_plane_calc(TransInfo *t, float r_plane[4])
r_plane[3] = -dot_v3v3(r_plane, t->center_global);
}
-static void constraintValuesFinal(TransInfo *t, float vec[3])
-{
- int mode = t->con.mode;
- if (mode & CON_APPLY) {
- float nval = (t->flag & T_NULL_ONE) ? 1.0f : 0.0f;
-
- if ((mode & CON_AXIS0) == 0) {
- vec[0] = nval;
- }
- if ((mode & CON_AXIS1) == 0) {
- vec[1] = nval;
- }
- if ((mode & CON_AXIS2) == 0) {
- vec[2] = nval;
- }
- }
-}
-
void constraintNumInput(TransInfo *t, float vec[3])
{
int mode = t->con.mode;
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 2ea78ca5377..baef2eb0d4b 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -2924,9 +2924,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
}
}
else if (use_edge && !pinned) {
- changed = true;
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
-
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -2934,29 +2931,18 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
- bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset);
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
- if ((select != luv_select) || (select != luv_select_prev)) {
- if (BLI_rctf_isect_pt_v(&rectf, luv->uv) &&
- BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
- BM_elem_flag_enable(l->v, BM_ELEM_TAG);
- BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
- }
+ if (BLI_rctf_isect_pt_v(&rectf, luv->uv) && BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
}
l_prev = l;
luv_prev = luv;
- luv_select_prev = luv_select;
}
}
-
- if (sima->sticky == SI_STICKY_VERTEX) {
- uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
- }
}
else {
/* other selection modes */
@@ -3155,8 +3141,6 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
}
}
else if (use_edge) {
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
-
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -3164,29 +3148,18 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
- bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset);
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
- if ((select != luv_select) || (select != luv_select_prev)) {
- if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) {
- changed = true;
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
- BM_elem_flag_enable(l->v, BM_ELEM_TAG);
- BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
- }
+ if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) {
+ uvedit_edge_select_set_with_sticky(
+ sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
}
l_prev = l;
luv_prev = luv;
- luv_select_prev = luv_select;
}
}
-
- if (sima->sticky == SI_STICKY_VERTEX) {
- uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
- }
}
else {
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
@@ -3346,8 +3319,6 @@ static bool do_lasso_select_mesh_uv(bContext *C,
}
}
else if (use_edge) {
- BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
-
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -3355,32 +3326,21 @@ static bool do_lasso_select_mesh_uv(bContext *C,
BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
- bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset);
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
- if ((select != luv_select) || (select != luv_select_prev)) {
- if (do_lasso_select_mesh_uv_is_point_inside(
- region, &rect, mcoords, mcoords_len, luv->uv) &&
- do_lasso_select_mesh_uv_is_point_inside(
- region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
- uvedit_uv_select_set(scene, em, l_prev, select, false, cd_loop_uv_offset);
- changed = true;
- BM_elem_flag_enable(l->v, BM_ELEM_TAG);
- BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG);
- }
+ if (do_lasso_select_mesh_uv_is_point_inside(
+ region, &rect, mcoords, mcoords_len, luv->uv) &&
+ do_lasso_select_mesh_uv_is_point_inside(
+ region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
}
l_prev = l;
luv_prev = luv;
- luv_select_prev = luv_select;
}
}
-
- if (sima->sticky == SI_STICKY_VERTEX) {
- uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
- }
}
else { /* Vert Sel */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc
index b63abb3d57f..9ab056e868d 100644
--- a/source/blender/gpu/intern/gpu_state.cc
+++ b/source/blender/gpu/intern/gpu_state.cc
@@ -319,7 +319,7 @@ void GPU_force_state(void)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Synchronisation Utils
+/** \name Synchronization Utils
* \{ */
void GPU_memory_barrier(eGPUBarrier barrier)
diff --git a/source/blender/imbuf/intern/cache.c b/source/blender/imbuf/intern/cache.c
index 23ce9bd7818..02d1fe3710a 100644
--- a/source/blender/imbuf/intern/cache.c
+++ b/source/blender/imbuf/intern/cache.c
@@ -32,6 +32,10 @@
#include "imbuf.h"
+/* -------------------------------------------------------------------- */
+/** \name Local Structs
+ * \{ */
+
/* We use a two level cache here. A per-thread cache with limited number of
* tiles. This can be accessed without locking and so is hoped to lead to most
* tile access being lock-free. The global cache is shared between all threads
@@ -85,7 +89,11 @@ typedef struct ImGlobalTileCache {
static ImGlobalTileCache GLOBAL_CACHE;
-/***************************** Hash Functions ********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hash Functions
+ * \{ */
static unsigned int imb_global_tile_hash(const void *gtile_p)
{
@@ -117,7 +125,11 @@ static bool imb_thread_tile_cmp(const void *a_p, const void *b_p)
return ((a->ibuf != b->ibuf) || (a->tx != b->tx) || (a->ty != b->ty));
}
-/******************************** Load/Unload ********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Load/Unload
+ * \{ */
static void imb_global_cache_tile_load(ImGlobalTile *gtile)
{
@@ -167,7 +179,11 @@ void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty)
BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
}
-/******************************* Init/Exit ***********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Init/Exit
+ * \{ */
static void imb_thread_cache_init(ImThreadTileCache *cache)
{
@@ -265,7 +281,11 @@ void IMB_tile_cache_params(int totthread, int maxmem)
BLI_mutex_init(&GLOBAL_CACHE.mutex);
}
-/***************************** Global Cache **********************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Global Cache
+ * \{ */
static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf,
int tx,
@@ -353,7 +373,11 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf,
return gtile;
}
-/***************************** Per-Thread Cache ******************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Per-Thread Cache
+ * \{ */
static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache,
ImBuf *ibuf,
@@ -465,3 +489,5 @@ void IMB_tiles_to_rect(ImBuf *ibuf)
}
}
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 6dd4d14cbc7..00dbd539410 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -60,7 +60,9 @@
#include <ocio_capi.h>
-/*********************** Global declarations *************************/
+/* -------------------------------------------------------------------- */
+/** \name Global declarations
+ * \{ */
#define DISPLAY_BUFFER_CHANNELS 4
@@ -135,9 +137,14 @@ static struct global_color_picking_state {
bool failed;
} global_color_picking_state = {NULL};
-/*********************** Color managed cache *************************/
+/** \} */
-/* Cache Implementation Notes
+/* -------------------------------------------------------------------- */
+/** \name Color Managed Cache
+ * \{ */
+
+/**
+ * Cache Implementation Notes
* ==========================
*
* All color management cache stuff is stored in two properties of
@@ -459,7 +466,11 @@ static void colormanage_cache_handle_release(void *cache_handle)
IMB_freeImBuf(cache_ibuf);
}
-/*********************** Initialization / De-initialization *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Initialization / De-initialization
+ * \{ */
static void colormanage_role_color_space_name_get(OCIO_ConstConfigRcPtr *config,
char *colorspace_name,
@@ -749,7 +760,11 @@ void colormanagement_exit(void)
colormanage_free_config();
}
-/*********************** Internal functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal functions
+ * \{ */
static bool colormanage_compatible_look(ColorManagedLook *look, const char *view_name)
{
@@ -1119,7 +1134,11 @@ void colormanage_imbuf_make_linear(ImBuf *ibuf, const char *from_colorspace)
}
}
-/*********************** Generic functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Functions
+ * \{ */
static void colormanage_check_display_settings(ColorManagedDisplaySettings *display_settings,
const char *what,
@@ -1459,7 +1478,11 @@ const float *IMB_colormangement_get_xyz_to_rgb()
return &imbuf_xyz_to_rgb[0][0];
}
-/*********************** Threaded display buffer transform routines *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Threaded Display Buffer Transform Routines
+ * \{ */
typedef struct DisplayBufferThread {
ColormanageProcessor *cm_processor;
@@ -1827,7 +1850,11 @@ static void colormanage_display_buffer_process(ImBuf *ibuf,
ibuf, NULL, display_buffer, view_settings, display_settings);
}
-/*********************** Threaded processor transform routines *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Threaded Processor Transform Routines
+ * \{ */
typedef struct ProcessorTransformThread {
ColormanageProcessor *cm_processor;
@@ -1955,7 +1982,11 @@ static void processor_transform_apply_threaded(unsigned char *byte_buffer,
do_processor_transform_thread);
}
-/*********************** Color space transformation functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Color Space Transformation Functions
+ * \{ */
/* Convert the whole buffer from specified by name color space to another -
* internal implementation. */
@@ -2667,7 +2698,11 @@ void IMB_colormanagement_buffer_make_display_space(
IMB_colormanagement_processor_free(cm_processor);
}
-/*********************** Public display buffers interfaces *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Display Buffers Interfaces
+ * \{ */
/* acquire display buffer for given image buffer using specified view and display settings */
unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
@@ -2825,7 +2860,11 @@ void IMB_display_buffer_release(void *cache_handle)
}
}
-/*********************** Display functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Display Functions
+ * \{ */
const char *colormanage_display_get_default_name(void)
{
@@ -2945,7 +2984,11 @@ const char *IMB_colormanagement_display_get_default_view_transform_name(
return colormanage_view_get_default_name(display);
}
-/*********************** View functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Functions
+ * \{ */
const char *colormanage_view_get_default_name(const ColorManagedDisplay *display)
{
@@ -3059,7 +3102,11 @@ const char *IMB_colormanagement_view_get_default_name(const char *display_name)
return NULL;
}
-/*********************** Color space functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Color Space Functions
+ * \{ */
static void colormanage_description_strip(char *description)
{
@@ -3203,7 +3250,11 @@ void IMB_colormanagement_colorspace_from_ibuf_ftype(
}
}
-/*********************** Looks functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Looks Functions
+ * \{ */
ColorManagedLook *colormanage_look_add(const char *name, const char *process_space, bool is_noop)
{
@@ -3276,7 +3327,11 @@ const char *IMB_colormanagement_look_get_indexed_name(int index)
return NULL;
}
-/*********************** RNA helper functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name RNA Helper Functions
+ * \{ */
void IMB_colormanagement_display_items_add(EnumPropertyItem **items, int *totitem)
{
@@ -3372,7 +3427,11 @@ void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *tot
}
}
-/*********************** Partial display buffer update *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Partial Display Buffer Update
+ * \{ */
/*
* Partial display update is supposed to be used by such areas as
@@ -3750,7 +3809,11 @@ void IMB_partial_display_buffer_update_delayed(ImBuf *ibuf, int xmin, int ymin,
}
}
-/*********************** Pixel processor functions *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pixel Processor Functions
+ * \{ */
ColormanageProcessor *IMB_colormanagement_display_processor_new(
const ColorManagedViewSettings *view_settings,
@@ -4172,3 +4235,5 @@ void IMB_colormanagement_finish_glsl_draw(void)
OCIO_finishGLSLDraw(global_glsl_state.ocio_glsl_state);
}
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index 798849f2dd4..5f580449e12 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -35,7 +35,9 @@
#include "MEM_guardedalloc.h"
-/************************* Floyd-Steinberg dithering *************************/
+/* -------------------------------------------------------------------- */
+/** \name Floyd-Steinberg dithering
+ * \{ */
typedef struct DitherContext {
float dither;
@@ -56,7 +58,11 @@ static void clear_dither_context(DitherContext *di)
MEM_freeN(di);
}
-/************************* Generic Buffer Conversion *************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Generic Buffer Conversion
+ * \{ */
MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4])
{
@@ -705,7 +711,11 @@ void IMB_buffer_byte_from_byte(uchar *rect_to,
}
}
-/****************************** ImBuf Conversion *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ImBuf Conversion
+ * \{ */
void IMB_rect_from_float(ImBuf *ibuf)
{
@@ -822,7 +832,11 @@ void IMB_float_from_rect(ImBuf *ibuf)
}
}
-/**************************** Color to Grayscale *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Color to Grayscale
+ * \{ */
/* no profile conversion */
void IMB_color_to_bw(ImBuf *ibuf)
@@ -864,7 +878,11 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height)
}
}
-/**************************** alter saturation *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Alter Saturation
+ * \{ */
void IMB_saturation(ImBuf *ibuf, float sat)
{
@@ -890,3 +908,5 @@ void IMB_saturation(ImBuf *ibuf, float sat)
}
}
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index bcac140f036..6ad69e72b4f 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -76,6 +76,7 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf)
}
}
}
+
static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y)
{
@@ -90,7 +91,9 @@ static void pixel_from_buffer(struct ImBuf *ibuf, unsigned char **outI, float **
}
}
-/* BICUBIC Interpolation */
+/* -------------------------------------------------------------------- */
+/** \name Bi-Cubic Interpolation
+ * \{ */
void bicubic_interpolation_color(
struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
@@ -118,7 +121,12 @@ void bicubic_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
bicubic_interpolation_color(in, outI, outF, u, v);
}
-/* BILINEAR INTERPOLATION */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bi-Linear Interpolation
+ * \{ */
+
void bilinear_interpolation_color(
struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
{
@@ -224,8 +232,13 @@ void bilinear_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, i
bilinear_interpolation_color(in, outI, outF, u, v);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Nearest Interpolation
+ * \{ */
+
/* function assumes out to be zero'ed, only does RGBA */
-/* NEAREST INTERPOLATION */
void nearest_interpolation_color(
struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
{
@@ -336,7 +349,9 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float u, float v, int xout, in
nearest_interpolation_color(in, outI, outF, u, v);
}
-/*********************** Threaded image processing *************************/
+/* -------------------------------------------------------------------- */
+/** \name Threaded Image Processing
+ * \{ */
static void processor_apply_func(TaskPool *__restrict pool, void *taskdata)
{
@@ -431,7 +446,11 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines,
BLI_task_pool_free(task_pool);
}
-/* Alpha-under */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Alpha-under
+ * \{ */
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3])
{
@@ -485,6 +504,12 @@ void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float b
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sample Pixel
+ * \{ */
+
/* Sample pixel of image using NEAREST method. */
void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4])
{
@@ -500,3 +525,5 @@ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_r
}
}
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c
index 247122065de..d3c91b55f22 100644
--- a/source/blender/imbuf/intern/stereoimbuf.c
+++ b/source/blender/imbuf/intern/stereoimbuf.c
@@ -58,6 +58,10 @@ typedef struct Stereo3DData {
bool is_float;
} Stereo3DData;
+/* -------------------------------------------------------------------- */
+/** \name Local Functions
+ * \{ */
+
static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode)
{
int x, y;
@@ -508,7 +512,11 @@ static void imb_stereo3d_write_topbottom(Stereo3DData *s3d)
}
}
-/**************************** dimension utils ****************************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Dimension Utils
+ * \{ */
void IMB_stereo3d_write_dimensions(const char mode,
const bool is_squeezed,
@@ -566,7 +574,11 @@ void IMB_stereo3d_read_dimensions(const char mode,
}
}
-/**************************** un/squeeze frame ****************************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Un/Squeeze Frame
+ * \{ */
static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf,
Stereo3dFormat *s3d,
@@ -667,7 +679,11 @@ static void imb_stereo3d_squeeze_rect(
IMB_freeImBuf(ibuf);
}
-/*************************** preparing to call the write functions **************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Preparing To Call The Write Functions
+ * \{ */
static void imb_stereo3d_data_init(Stereo3DData *s3d_data,
const bool is_float,
@@ -798,7 +814,11 @@ static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d)
}
}
-/******************************** reading stereo imbufs **********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Reading Stereo ImBuf's
+ * \{ */
static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode)
{
@@ -1249,7 +1269,11 @@ static void imb_stereo3d_read_topbottom(Stereo3DData *s3d)
}
}
-/*************************** preparing to call the read functions **************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Preparing To Call The Read Functions
+ * \{ */
/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */
void IMB_ImBufFromStereo3d(Stereo3dFormat *s3d,
@@ -1351,3 +1375,5 @@ static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d)
break;
}
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 9d5d7f58335..0c235445920 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -57,9 +57,10 @@
# include "utfconv.h"
#endif
-/***********************
- * Local declarations. *
- ***********************/
+/* -------------------------------------------------------------------- */
+/** \name Local Declarations
+ * \{ */
+
/* Reading and writing of an in-memory TIFF file. */
static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n);
static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n);
@@ -69,17 +70,22 @@ static toff_t imb_tiff_SizeProc(thandle_t handle);
static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize);
static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size);
-/* Structure for in-memory TIFF file. */
+/** Structure for in-memory TIFF file. */
typedef struct ImbTIFFMemFile {
- const unsigned char *mem; /* Location of first byte of TIFF file. */
- toff_t offset; /* Current offset within the file. */
- tsize_t size; /* Size of the TIFF file. */
+ /** Location of first byte of TIFF file. */
+ const unsigned char *mem;
+ /** Current offset within the file. */
+ toff_t offset;
+ /** Size of the TIFF file. */
+ tsize_t size;
} ImbTIFFMemFile;
#define IMB_TIFF_GET_MEMFILE(x) ((ImbTIFFMemFile *)(x))
-/*****************************
- * Function implementations. *
- *****************************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Function Implementations
+ * \{ */
static void imb_tiff_DummyUnmapProc(
thandle_t fd,
@@ -111,8 +117,8 @@ static int imb_tiff_DummyMapProc(
* Reads data from an in-memory TIFF file.
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
- * \param data: Buffer to contain data (treat as (void *)).
- * \param n: Number of bytes to read.
+ * \param data: Buffer to contain data (treat as (void *)).
+ * \param n: Number of bytes to read.
*
* \return: Number of bytes actually read.
* 0 = EOF.
@@ -174,8 +180,8 @@ static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n)
/**
* Seeks to a new location in an in-memory TIFF file.
*
- * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile).
- * \param ofs: Offset value (interpreted according to whence below).
+ * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
+ * \param ofs: Offset value (interpreted according to whence below).
* \param whence: This can be one of three values:
* SEEK_SET - The offset is set to ofs bytes.
* SEEK_CUR - The offset is set to its current location plus ofs bytes.
@@ -226,7 +232,7 @@ static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence)
* are made to access the file after that point. However, no such
* attempts should ever be made (in theory).
*
- * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile).
+ * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
*
* \return: 0
*/
@@ -288,6 +294,12 @@ static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char *
imb_tiff_DummyUnmapProc);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Load TIFF
+ * \{ */
+
/**
* Checks whether a given memory buffer contains a TIFF file.
*
@@ -539,10 +551,10 @@ void imb_inittiff(void)
/**
* Loads a TIFF file.
- * \param mem: Memory containing the TIFF file.
- * \param size: Size of the mem buffer.
+ * \param mem: Memory containing the TIFF file.
+ * \param size: Size of the mem buffer.
* \param flags: If flags has IB_test set then the file is not actually loaded,
- * but all other operations take place.
+ * but all other operations take place.
*
* \return: A newly allocated ImBuf structure if successful, otherwise NULL.
*/
@@ -726,17 +738,23 @@ void imb_loadtiletiff(
TIFFClose(image);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Save TIFF
+ * \{ */
+
/**
* Saves a TIFF file.
*
- * ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA
+ * #ImBuf structures with 1, 3 or 4 bytes per pixel (GRAY, RGB, RGBA
* respectively) are accepted, and interpreted correctly. Note that the TIFF
* convention is to use pre-multiplied alpha, which can be achieved within
* Blender by setting "Premul" alpha handling. Other alpha conventions are
* not strictly correct, but are permitted anyhow.
*
- * \param ibuf: Image buffer.
- * \param name: Name of the TIFF file to create.
+ * \param ibuf: Image buffer.
+ * \param name: Name of the TIFF file to create.
* \param flags: Currently largely ignored.
*
* \return: 1 if the function is successful, 0 on failure.
@@ -958,3 +976,5 @@ int imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
}
return 1;
}
+
+/** \} */
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 9a2c74c64a3..9785f6d68ab 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -60,6 +60,7 @@ struct AlembicExportParams {
bool triangulate;
bool export_hair;
bool export_particles;
+ bool export_custom_properties;
bool use_instancing;
/* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index 2b44146e475..d55f2382a9b 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -56,6 +56,7 @@ set(SRC
intern/alembic_capi.cc
exporter/abc_archive.cc
+ exporter/abc_custom_props.cc
exporter/abc_export_capi.cc
exporter/abc_hierarchy_iterator.cc
exporter/abc_subdiv_disabler.cc
@@ -84,6 +85,7 @@ set(SRC
intern/abc_util.h
exporter/abc_archive.h
+ exporter/abc_custom_props.h
exporter/abc_hierarchy_iterator.h
exporter/abc_subdiv_disabler.h
exporter/abc_writer_abstract.h
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
new file mode 100644
index 00000000000..382afdc294d
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#include "abc_custom_props.h"
+
+#include "abc_writer_abstract.h"
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+
+#include <Alembic/Abc/OTypedArrayProperty.h>
+#include <Alembic/Abc/OTypedScalarProperty.h>
+
+#include "BKE_idprop.h"
+#include "DNA_ID.h"
+
+using Alembic::Abc::ArraySample;
+using Alembic::Abc::OArrayProperty;
+using Alembic::Abc::OBoolArrayProperty;
+using Alembic::Abc::OCompoundProperty;
+using Alembic::Abc::ODoubleArrayProperty;
+using Alembic::Abc::OFloatArrayProperty;
+using Alembic::Abc::OInt32ArrayProperty;
+using Alembic::Abc::OStringArrayProperty;
+
+namespace blender::io::alembic {
+
+CustomPropertiesExporter::CustomPropertiesExporter(ABCAbstractWriter *owner) : owner_(owner)
+{
+}
+
+CustomPropertiesExporter::~CustomPropertiesExporter()
+{
+}
+
+void CustomPropertiesExporter::write_all(const IDProperty *group)
+{
+ if (group == nullptr) {
+ return;
+ }
+ BLI_assert(group->type == IDP_GROUP);
+
+ /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
+ LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
+ if (STREQ(id_property->name, "_RNA_UI")) {
+ continue;
+ }
+ write(id_property);
+ }
+}
+
+void CustomPropertiesExporter::write(const IDProperty *id_property)
+{
+ BLI_assert(id_property->name[0] != '\0');
+
+ switch (id_property->type) {
+ case IDP_STRING: {
+ /* The Alembic library doesn't accept NULL-terminated character arrays. */
+ const std::string prop_value(IDP_String(id_property), id_property->len - 1);
+ set_scalar_property<OStringArrayProperty, std::string>(id_property->name, prop_value);
+ break;
+ }
+ case IDP_INT:
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ set_scalar_property<OInt32ArrayProperty, int32_t>(id_property->name, IDP_Int(id_property));
+ break;
+ case IDP_FLOAT:
+ set_scalar_property<OFloatArrayProperty, float>(id_property->name, IDP_Float(id_property));
+ break;
+ case IDP_DOUBLE:
+ set_scalar_property<ODoubleArrayProperty, double>(id_property->name,
+ IDP_Double(id_property));
+ break;
+ case IDP_ARRAY:
+ write_array(id_property);
+ break;
+ case IDP_IDPARRAY:
+ write_idparray(id_property);
+ break;
+ }
+}
+
+void CustomPropertiesExporter::write_array(const IDProperty *id_property)
+{
+ BLI_assert(id_property->type == IDP_ARRAY);
+
+ switch (id_property->subtype) {
+ case IDP_INT: {
+ const int *array = (int *)IDP_Array(id_property);
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ set_array_property<OInt32ArrayProperty, int32_t>(id_property->name, array, id_property->len);
+ break;
+ }
+ case IDP_FLOAT: {
+ const float *array = (float *)IDP_Array(id_property);
+ set_array_property<OFloatArrayProperty, float>(id_property->name, array, id_property->len);
+ break;
+ }
+ case IDP_DOUBLE: {
+ const double *array = (double *)IDP_Array(id_property);
+ set_array_property<ODoubleArrayProperty, double>(id_property->name, array, id_property->len);
+ break;
+ }
+ }
+}
+
+void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+
+ if (idp_array->len == 0) {
+ /* Don't bother writing dataless arrays. */
+ return;
+ }
+
+ IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
+
+#ifndef NDEBUG
+ /* Sanity check that all elements of the array have the same type.
+ * Blender should already enforce this, hence it's only used in debug mode. */
+ for (int i = 1; i < idp_array->len; i++) {
+ if (idp_elements[i].type == idp_elements[0].type) {
+ continue;
+ }
+ std::cerr << "Custom property " << idp_array->name << " has elements of varying type";
+ BLI_assert(!"Mixed type IDP_ARRAY custom property found");
+ }
+#endif
+
+ switch (idp_elements[0].type) {
+ case IDP_STRING:
+ write_idparray_of_strings(idp_array);
+ break;
+ case IDP_ARRAY:
+ write_idparray_of_numbers(idp_array);
+ break;
+ }
+}
+
+void CustomPropertiesExporter::write_idparray_of_strings(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ /* Convert to an array of std::strings, because Alembic doesn't like zero-delimited strings. */
+ IDProperty *idp_elements = (IDProperty *)IDP_Array(idp_array);
+ std::vector<std::string> strings(idp_array->len);
+ for (int i = 0; i < idp_array->len; i++) {
+ BLI_assert(idp_elements[i].type == IDP_STRING);
+ strings[i] = IDP_String(&idp_elements[i]);
+ }
+
+ /* Alembic needs a pointer to the first value of the array. */
+ const std::string *array_of_strings = &strings[0];
+ set_array_property<OStringArrayProperty, std::string>(
+ idp_array->name, array_of_strings, strings.size());
+}
+
+void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ /* This must be an array of arrays. */
+ IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
+ BLI_assert(idp_rows[0].type == IDP_ARRAY);
+
+ const int subtype = idp_rows[0].subtype;
+ if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) {
+ /* Non-numerical types are not supported. */
+ return;
+ }
+
+ switch (subtype) {
+ case IDP_INT:
+ static_assert(sizeof(int) == sizeof(int32_t), "Expecting 'int' to be 32-bit");
+ write_idparray_flattened_typed<OInt32ArrayProperty, int32_t>(idp_array);
+ break;
+ case IDP_FLOAT:
+ write_idparray_flattened_typed<OFloatArrayProperty, float>(idp_array);
+ break;
+ case IDP_DOUBLE:
+ write_idparray_flattened_typed<ODoubleArrayProperty, double>(idp_array);
+ break;
+ }
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty *idp_array)
+{
+ BLI_assert(idp_array->type == IDP_IDPARRAY);
+ BLI_assert(idp_array->len > 0);
+
+ const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array);
+ BLI_assert(idp_rows[0].type == IDP_ARRAY);
+ BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE));
+
+ const uint64_t num_rows = idp_array->len;
+ std::vector<BlenderValueType> matrix_values;
+ for (size_t row_idx = 0; row_idx < num_rows; ++row_idx) {
+ const BlenderValueType *row = (BlenderValueType *)IDP_Array(&idp_rows[row_idx]);
+ for (size_t col_idx = 0; col_idx < idp_rows[row_idx].len; col_idx++) {
+ matrix_values.push_back(row[col_idx]);
+ }
+ }
+
+ set_array_property<ABCPropertyType, BlenderValueType>(
+ idp_array->name, &matrix_values[0], matrix_values.size());
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::set_scalar_property(const StringRef property_name,
+ const BlenderValueType property_value)
+{
+ set_array_property<ABCPropertyType, BlenderValueType>(property_name, &property_value, 1);
+}
+
+template<typename ABCPropertyType, typename BlenderValueType>
+void CustomPropertiesExporter::set_array_property(const StringRef property_name,
+ const BlenderValueType *array_values,
+ const size_t num_array_items)
+{
+ auto create_callback = [this, property_name]() -> OArrayProperty {
+ return create_abc_property<ABCPropertyType>(property_name);
+ };
+
+ OArrayProperty array_prop = abc_properties_.lookup_or_add_cb(property_name, create_callback);
+ Alembic::Util::Dimensions array_dimensions(num_array_items);
+ ArraySample sample(array_values, array_prop.getDataType(), array_dimensions);
+ array_prop.set(sample);
+}
+
+template<typename ABCPropertyType>
+OArrayProperty CustomPropertiesExporter::create_abc_property(const StringRef property_name)
+{
+ /* Get the necessary info from our owner. */
+ OCompoundProperty abc_prop_for_custom_props = owner_->abc_prop_for_custom_props();
+ const uint32_t timesample_index = owner_->timesample_index();
+
+ /* Construct the Alembic property. */
+ ABCPropertyType abc_property(abc_prop_for_custom_props, property_name);
+ abc_property.setTimeSampling(timesample_index);
+ return abc_property;
+}
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.h b/source/blender/io/alembic/exporter/abc_custom_props.h
new file mode 100644
index 00000000000..d3f9b2fc43c
--- /dev/null
+++ b/source/blender/io/alembic/exporter/abc_custom_props.h
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup Alembic
+ */
+
+#pragma once
+
+#include <Alembic/Abc/OArrayProperty.h>
+#include <Alembic/Abc/OCompoundProperty.h>
+
+#include "BLI_map.hh"
+
+#include <memory>
+
+struct IDProperty;
+
+namespace blender::io::alembic {
+
+class ABCAbstractWriter;
+
+/* Write values of Custom Properties (a.k.a. ID Properties) to Alembic.
+ *
+ * Each Alembic Writer instance optionally has one CustomPropertiesExporter (CPE). This CPE not
+ * only writes the custom properties to Alembic, but also keeps references in memory so that the
+ * Alembic library doesn't prematurely finalize the data. */
+class CustomPropertiesExporter {
+ private:
+ /* Owner is used to get the OCompoundProperty and time sample index. The former should only be
+ * requested from the Alembic library when it's actually going to be used to add custom
+ * properties (otherwise an invalid Alembic file is written). */
+ ABCAbstractWriter *owner_;
+
+ /* The Compound Property that will contain the exported custom properties.
+ *
+ * Typically this the return value of abc_schema.getArbGeomParams() or
+ * abc_schema.getUserProperties(). */
+ Alembic::Abc::OCompoundProperty abc_compound_prop_;
+
+ /* Mapping from property name in Blender to property in Alembic.
+ * Here Blender does the same as other software (Maya, Houdini), and writes
+ * scalar properties as single-element arrays. */
+ Map<std::string, Alembic::Abc::OArrayProperty> abc_properties_;
+
+ public:
+ CustomPropertiesExporter(ABCAbstractWriter *owner);
+ virtual ~CustomPropertiesExporter();
+
+ void write_all(const IDProperty *group);
+
+ private:
+ void write(const IDProperty *id_property);
+ void write_array(const IDProperty *id_property);
+
+ /* IDProperty arrays are used to store arrays-of-arrays or arrays-of-strings. */
+ void write_idparray(const IDProperty *idp_array);
+ void write_idparray_of_strings(const IDProperty *idp_array);
+ void write_idparray_of_numbers(const IDProperty *idp_array);
+
+ /* Flatten an array-of-arrays into one long array, then write that.
+ * It is tempting to write an array of NxM numbers as a matrix, but there is
+ * no guarantee that the data actually represents a matrix. */
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void write_idparray_flattened_typed(const IDProperty *idp_array);
+
+ /* Write a single scalar (i.e. non-array) property as single-value array. */
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void set_scalar_property(StringRef property_name, const BlenderValueType property_value);
+
+ template<typename ABCPropertyType, typename BlenderValueType>
+ void set_array_property(StringRef property_name,
+ const BlenderValueType *array_values,
+ size_t num_array_items);
+
+ template<typename ABCPropertyType>
+ Alembic::Abc::OArrayProperty create_abc_property(StringRef property_name);
+};
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
index 0b08d8c4e58..e99048cc0ee 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc
@@ -59,6 +59,7 @@ void ABCAbstractWriter::write(HierarchyContext &context)
if (!frame_has_been_written_) {
is_animated_ = (args_.export_params->frame_start != args_.export_params->frame_end) &&
check_is_animated(context);
+ ensure_custom_properties_exporter(context);
}
else if (!is_animated_) {
/* A frame has already been written, and without animation one frame is enough. */
@@ -67,9 +68,49 @@ void ABCAbstractWriter::write(HierarchyContext &context)
do_write(context);
+ if (custom_props_) {
+ custom_props_->write_all(get_id_properties(context));
+ }
+
frame_has_been_written_ = true;
}
+void ABCAbstractWriter::ensure_custom_properties_exporter(const HierarchyContext &context)
+{
+ if (!args_.export_params->export_custom_properties) {
+ return;
+ }
+
+ if (custom_props_) {
+ /* Custom properties exporter already created. */
+ return;
+ }
+
+ /* Avoid creating a custom properties exporter if there are no custom properties to export. */
+ const IDProperty *id_properties = get_id_properties(context);
+ if (id_properties == nullptr || id_properties->len == 0) {
+ return;
+ }
+
+ custom_props_ = std::make_unique<CustomPropertiesExporter>(this);
+}
+
+const IDProperty *ABCAbstractWriter::get_id_properties(const HierarchyContext &context) const
+{
+ Object *object = context.object;
+ if (object->data == nullptr) {
+ return nullptr;
+ }
+
+ /* Most subclasses write object data, so default to the object data's ID properties. */
+ return static_cast<ID *>(object->data)->properties;
+}
+
+uint32_t ABCAbstractWriter::timesample_index() const
+{
+ return timesample_index_;
+}
+
const Imath::Box3d &ABCAbstractWriter::bounding_box() const
{
return bounding_box_;
diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.h b/source/blender/io/alembic/exporter/abc_writer_abstract.h
index 59c55330443..d23e69cf73e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_abstract.h
+++ b/source/blender/io/alembic/exporter/abc_writer_abstract.h
@@ -19,6 +19,7 @@
#pragma once
#include "IO_abstract_hierarchy_iterator.h"
+#include "abc_custom_props.h"
#include "abc_hierarchy_iterator.h"
#include <Alembic/Abc/OObject.h>
@@ -27,6 +28,7 @@
#include "DEG_depsgraph_query.h"
#include "DNA_material_types.h"
+struct IDProperty;
struct Material;
struct Object;
@@ -44,6 +46,9 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
/* Visibility of this writer's data in Alembic. */
Alembic::Abc::OCharProperty abc_visibility_;
+ /* Optional writer for custom properties. */
+ std::unique_ptr<CustomPropertiesExporter> custom_props_;
+
public:
explicit ABCAbstractWriter(const ABCWriterConstructorArgs &args);
virtual ~ABCAbstractWriter();
@@ -59,6 +64,7 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
* Empty). */
virtual bool is_supported(const HierarchyContext *context) const;
+ uint32_t timesample_index() const;
const Imath::Box3d &bounding_box() const;
/* Called by AlembicHierarchyCreator after checking that the data is supported via
@@ -67,12 +73,47 @@ class ABCAbstractWriter : public AbstractHierarchyWriter {
virtual Alembic::Abc::OObject get_alembic_object() const = 0;
+ /* Return the Alembic object's CompoundProperty that'll contain the custom properties.
+ *
+ * This function is called whenever there are custom properties to be written to Alembic. It
+ * should call abc_schema_prop_for_custom_props() with the writer's Alembic schema object.
+ *
+ * If custom properties are not supported by a specific subclass, it should return an empty
+ * OCompoundProperty() and override ensure_custom_properties_exporter() to do nothing.
+ */
+ virtual Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() = 0;
+
protected:
virtual void do_write(HierarchyContext &context) = 0;
virtual void update_bounding_box(Object *object);
+ /* Return ID properties of whatever ID datablock is written by this writer. Defaults to the
+ * properties of the object data. Can return nullptr if no custom properties are to be written.
+ */
+ virtual const IDProperty *get_id_properties(const HierarchyContext &context) const;
+
+ virtual void ensure_custom_properties_exporter(const HierarchyContext &context);
+
void write_visibility(const HierarchyContext &context);
+
+ /* Return the Alembic schema's compound property, which will be used for writing custom
+ * properties.
+ *
+ * This can return either abc_schema.getUserProperties() or abc_schema.getArbGeomParams(). The
+ * former only holds values similar to Blender's custom properties, whereas the latter can also
+ * specify that certain custom properties vary per mesh component (so per face, vertex, etc.). As
+ * such, .userProperties is more suitable for custom properties. However, Maya, Houdini use
+ * .arbGeomParams for custom data.
+ *
+ * Because of this, the code uses this templated function so that there is one place that
+ * determines where custom properties are exporter to.
+ */
+ template<typename T>
+ Alembic::Abc::OCompoundProperty abc_schema_prop_for_custom_props(T abc_schema)
+ {
+ return abc_schema.getUserProperties();
+ }
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.cc b/source/blender/io/alembic/exporter/abc_writer_camera.cc
index 0ce6c3dc07f..82a741699e4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.cc
@@ -65,6 +65,11 @@ Alembic::Abc::OObject ABCCameraWriter::get_alembic_object() const
return abc_camera_;
}
+Alembic::Abc::OCompoundProperty ABCCameraWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_camera_schema_);
+}
+
void ABCCameraWriter::do_write(HierarchyContext &context)
{
Camera *cam = static_cast<Camera *>(context.object->data);
diff --git a/source/blender/io/alembic/exporter/abc_writer_camera.h b/source/blender/io/alembic/exporter/abc_writer_camera.h
index 1b3e5898517..41885ed66d6 100644
--- a/source/blender/io/alembic/exporter/abc_writer_camera.h
+++ b/source/blender/io/alembic/exporter/abc_writer_camera.h
@@ -43,6 +43,7 @@ class ABCCameraWriter : public ABCAbstractWriter {
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.cc b/source/blender/io/alembic/exporter/abc_writer_curves.cc
index 6a12e4c59f3..b57af345a3c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.cc
@@ -66,6 +66,11 @@ Alembic::Abc::OObject ABCCurveWriter::get_alembic_object() const
return abc_curve_;
}
+Alembic::Abc::OCompoundProperty ABCCurveWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_curve_schema_);
+}
+
void ABCCurveWriter::do_write(HierarchyContext &context)
{
Curve *curve = static_cast<Curve *>(context.object->data);
diff --git a/source/blender/io/alembic/exporter/abc_writer_curves.h b/source/blender/io/alembic/exporter/abc_writer_curves.h
index d15f008f947..e210363557c 100644
--- a/source/blender/io/alembic/exporter/abc_writer_curves.h
+++ b/source/blender/io/alembic/exporter/abc_writer_curves.h
@@ -41,6 +41,7 @@ class ABCCurveWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
protected:
virtual void do_write(HierarchyContext &context) override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index 80034245b84..072feb2a90a 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -62,6 +62,11 @@ Alembic::Abc::OObject ABCHairWriter::get_alembic_object() const
return abc_curves_;
}
+Alembic::Abc::OCompoundProperty ABCHairWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_curves_schema_);
+}
+
bool ABCHairWriter::check_is_animated(const HierarchyContext & /*context*/) const
{
/* We assume that hair particles are always animated. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.h b/source/blender/io/alembic/exporter/abc_writer_hair.h
index f7d988ecbc4..3759ffa4310 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.h
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.h
@@ -44,6 +44,7 @@ class ABCHairWriter : public ABCAbstractWriter {
protected:
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
private:
void write_hair_sample(const HierarchyContext &context,
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc
index 14c65e2a2e2..7f3b044cb8b 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc
@@ -50,6 +50,16 @@ void ABCInstanceWriter::create_alembic_objects(const HierarchyContext *context)
CLOG_INFO(&LOG, 2, "exporting instance %s", args_.abc_path.c_str());
}
+void ABCInstanceWriter::ensure_custom_properties_exporter(const HierarchyContext & /*context*/)
+{
+ /* Intentionally do nothing. Instances should not have their own custom properties. */
+}
+
+Alembic::Abc::OCompoundProperty ABCInstanceWriter::abc_prop_for_custom_props()
+{
+ return Alembic::Abc::OCompoundProperty();
+}
+
OObject ABCInstanceWriter::get_alembic_object() const
{
/* There is no OObject for an instance. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.h b/source/blender/io/alembic/exporter/abc_writer_instance.h
index 067c4af7aed..f7d6450055a 100644
--- a/source/blender/io/alembic/exporter/abc_writer_instance.h
+++ b/source/blender/io/alembic/exporter/abc_writer_instance.h
@@ -39,6 +39,8 @@ class ABCInstanceWriter : public ABCAbstractWriter {
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
+ void ensure_custom_properties_exporter(const HierarchyContext &context) override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index b762ad47932..fbc662113cc 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -125,6 +125,14 @@ Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const
return abc_poly_mesh_;
}
+Alembic::Abc::OCompoundProperty ABCGenericMeshWriter::abc_prop_for_custom_props()
+{
+ if (is_subd_) {
+ return abc_schema_prop_for_custom_props(abc_subdiv_schema_);
+ }
+ return abc_schema_prop_for_custom_props(abc_poly_mesh_schema_);
+}
+
bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const
{
ModifierData *md = static_cast<ModifierData *>(ob_eval->modifiers.last);
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h
index 956587df7c0..fdf2d3cc1e3 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.h
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h
@@ -55,6 +55,7 @@ class ABCGenericMeshWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
protected:
virtual bool is_supported(const HierarchyContext *context) const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
index de1870fefd9..7595a0eba63 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc
@@ -78,6 +78,17 @@ OObject ABCNurbsWriter::get_alembic_object() const
return abc_nurbs_[0];
}
+Alembic::Abc::OCompoundProperty ABCNurbsWriter::abc_prop_for_custom_props()
+{
+ if (abc_nurbs_.empty()) {
+ return Alembic::Abc::OCompoundProperty();
+ }
+
+ /* A single NURBS object in Blender is expanded to multiple curves in Alembic.
+ * Just store the custom properties on the first one for simplicity. */
+ return abc_schema_prop_for_custom_props(abc_nurbs_schemas_[0]);
+}
+
bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const
{
/* Check if object has shape keys. */
diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.h b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
index 691390ffc9f..0f206d34682 100644
--- a/source/blender/io/alembic/exporter/abc_writer_nurbs.h
+++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.h
@@ -40,6 +40,7 @@ class ABCNurbsWriter : public ABCAbstractWriter {
virtual bool is_supported(const HierarchyContext *context) const override;
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
class ABCNurbsMeshWriter : public ABCGenericMeshWriter {
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.cc b/source/blender/io/alembic/exporter/abc_writer_points.cc
index 83d33577b3b..557f580e8aa 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_points.cc
@@ -58,6 +58,11 @@ Alembic::Abc::OObject ABCPointsWriter::get_alembic_object() const
return abc_points_;
}
+Alembic::Abc::OCompoundProperty ABCPointsWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props(abc_points_schema_);
+}
+
bool ABCPointsWriter::is_supported(const HierarchyContext *context) const
{
return ELEM(context->particle_system->part->type,
diff --git a/source/blender/io/alembic/exporter/abc_writer_points.h b/source/blender/io/alembic/exporter/abc_writer_points.h
index fec5e84f3f2..0447c41db3e 100644
--- a/source/blender/io/alembic/exporter/abc_writer_points.h
+++ b/source/blender/io/alembic/exporter/abc_writer_points.h
@@ -37,6 +37,7 @@ class ABCPointsWriter : public ABCAbstractWriter {
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
virtual bool is_supported(const HierarchyContext *context) const override;
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.cc b/source/blender/io/alembic/exporter/abc_writer_transform.cc
index a72a6b47aa9..79e460e56e9 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.cc
@@ -53,6 +53,17 @@ void ABCTransformWriter::create_alembic_objects(const HierarchyContext * /*conte
abc_xform_schema_ = abc_xform_.getSchema();
}
+Alembic::Abc::OCompoundProperty ABCTransformWriter::abc_prop_for_custom_props()
+{
+ return abc_schema_prop_for_custom_props<OXformSchema>(abc_xform_schema_);
+}
+
+const IDProperty *ABCTransformWriter::get_id_properties(const HierarchyContext &context) const
+{
+ const Object *object = context.object;
+ return object->id.properties;
+}
+
void ABCTransformWriter::do_write(HierarchyContext &context)
{
float parent_relative_matrix[4][4]; // The object matrix relative to the parent.
diff --git a/source/blender/io/alembic/exporter/abc_writer_transform.h b/source/blender/io/alembic/exporter/abc_writer_transform.h
index a334fe610ee..4542b9de506 100644
--- a/source/blender/io/alembic/exporter/abc_writer_transform.h
+++ b/source/blender/io/alembic/exporter/abc_writer_transform.h
@@ -21,6 +21,8 @@
#include "abc_writer_abstract.h"
+#include <memory>
+
#include <Alembic/AbcGeom/OXform.h>
namespace blender::io::alembic {
@@ -38,6 +40,8 @@ class ABCTransformWriter : public ABCAbstractWriter {
virtual void do_write(HierarchyContext &context) override;
virtual bool check_is_animated(const HierarchyContext &context) const override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
+ const IDProperty *get_id_properties(const HierarchyContext &context) const override;
+ Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index a9f1d5bcfc4..9a5b5b8c2ca 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -861,26 +861,32 @@ typedef struct BooleanModifierData {
ModifierData modifier;
struct Object *object;
+ struct Collection *collection;
+ float double_threshold;
char operation;
char solver;
char flag;
char bm_flag;
- float double_threshold;
} BooleanModifierData;
+/* BooleanModifierData->operation */
typedef enum {
eBooleanModifierOp_Intersect = 0,
eBooleanModifierOp_Union = 1,
eBooleanModifierOp_Difference = 2,
} BooleanModifierOp;
+/* BooleanModifierData->solver */
typedef enum {
eBooleanModifierSolver_Fast = 0,
eBooleanModifierSolver_Exact = 1,
} BooleanModifierSolver;
+/* BooleanModifierData->flag */
enum {
eBooleanModifierFlag_Self = (1 << 0),
+ eBooleanModifierFlag_Object = (1 << 1),
+ eBooleanModifierFlag_Collection = (1 << 2),
};
/* bm_flag only used when G_DEBUG. */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 56966ca26d3..24d28bf45de 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -165,7 +165,8 @@ typedef struct SpaceProperties {
void *texuser;
- SpaceProperties_Runtime runtime;
+ /* Doesn't necessarily need to be a pointer, but runtime structs are still written to files. */
+ SpaceProperties_Runtime *runtime;
} SpaceProperties;
/* button defines (deprecated) */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 29e29961028..f5a35783dca 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -420,10 +420,10 @@ static int add_name(const char *str)
}
if (str[0] == '(' && str[1] == '*') {
- /* we handle function pointer and special array cases here, e.g.
- * void (*function)(...) and float (*array)[..]. the array case
+ /* We handle function pointer and special array cases here, e.g.
+ * `void (*function)(...)` and `float (*array)[..]`. the array case
* name is still converted to (array *)() though because it is that
- * way in old dna too, and works correct with elementsize() */
+ * way in old DNA too, and works correct with #DNA_elem_size_nr. */
int isfuncptr = (strchr(str + 1, '(')) != NULL;
DEBUG_PRINTF(3, "\t\t\t\t*** Function pointer or multidim array pointer found\n");
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 155943b3b8d..c0a46c65969 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -886,6 +886,8 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
}
RNA_def_property_update(prop, 0, "rna_Bone_update_renamed");
+ RNA_define_lib_overridable(true);
+
/* flags */
prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER);
RNA_def_property_boolean_sdna(prop, NULL, "layer", 1);
@@ -1116,6 +1118,8 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_PTR_NO_OWNERSHIP);
RNA_def_property_ui_text(
prop, "B-Bone End Handle", "Bone that serves as the end handle for the B-Bone curve");
+
+ RNA_define_lib_overridable(false);
}
/* err... bones should not be directly edited (only editbones should be...) */
@@ -1149,6 +1153,8 @@ static void rna_def_bone(BlenderRNA *brna)
rna_def_bone_common(srna, 0);
rna_def_bone_curved_common(srna, false, false);
+ RNA_define_lib_overridable(true);
+
/* XXX should we define this in PoseChannel wrapping code instead?
* But PoseChannels directly get some of their flags from here... */
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
@@ -1231,6 +1237,8 @@ static void rna_def_bone(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Length", "Length of the bone");
+ RNA_define_lib_overridable(false);
+
RNA_api_bone(srna);
}
@@ -1461,6 +1469,8 @@ static void rna_def_armature(BlenderRNA *brna)
/* Animation Data */
rna_def_animdata_common(srna);
+ RNA_define_lib_overridable(true);
+
/* Collections */
prop = RNA_def_property(srna, "bones", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "bonebase", NULL);
@@ -1554,6 +1564,8 @@ static void rna_def_armature(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, "rna_Armature_is_editmode_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Is Editmode", "True when used in editmode");
+
+ RNA_define_lib_overridable(false);
}
void RNA_def_armature(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index da1ed166eb2..ed0fe3f7765 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -43,6 +43,8 @@
# include "BLI_iterator.h"
# include "BLI_math.h"
+# include "RNA_access.h"
+
# include "BKE_duplilist.h"
# include "BKE_object.h"
# include "BKE_scene.h"
@@ -461,14 +463,19 @@ static PointerRNA rna_Depsgraph_scene_get(PointerRNA *ptr)
{
Depsgraph *depsgraph = (Depsgraph *)ptr->data;
Scene *scene = DEG_get_input_scene(depsgraph);
- return rna_pointer_inherit_refine(ptr, &RNA_Scene, scene);
+ PointerRNA newptr;
+ RNA_pointer_create(&scene->id, &RNA_Scene, scene, &newptr);
+ return newptr;
}
static PointerRNA rna_Depsgraph_view_layer_get(PointerRNA *ptr)
{
Depsgraph *depsgraph = (Depsgraph *)ptr->data;
+ Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- return rna_pointer_inherit_refine(ptr, &RNA_ViewLayer, view_layer);
+ PointerRNA newptr;
+ RNA_pointer_create(&scene->id, &RNA_ViewLayer, view_layer, &newptr);
+ return newptr;
}
static PointerRNA rna_Depsgraph_scene_eval_get(PointerRNA *ptr)
@@ -476,13 +483,19 @@ static PointerRNA rna_Depsgraph_scene_eval_get(PointerRNA *ptr)
Depsgraph *depsgraph = (Depsgraph *)ptr->data;
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
return rna_pointer_inherit_refine(ptr, &RNA_Scene, scene_eval);
+ PointerRNA newptr;
+ RNA_pointer_create(&scene_eval->id, &RNA_Scene, scene_eval, &newptr);
+ return newptr;
}
static PointerRNA rna_Depsgraph_view_layer_eval_get(PointerRNA *ptr)
{
Depsgraph *depsgraph = (Depsgraph *)ptr->data;
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- return rna_pointer_inherit_refine(ptr, &RNA_ViewLayer, view_layer_eval);
+ PointerRNA newptr;
+ RNA_pointer_create(&scene_eval->id, &RNA_ViewLayer, view_layer_eval, &newptr);
+ return newptr;
}
#else
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index 0a58f8af593..2d26317c00b 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -2582,7 +2582,7 @@ static void rna_def_fluid_flow_settings(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_flow_reset");
prop = RNA_def_property(srna, "particle_size", PROP_FLOAT, PROP_NONE);
- RNA_def_property_range(prop, 0.1, 20.0);
+ RNA_def_property_range(prop, 0.1, FLT_MAX);
RNA_def_property_ui_range(prop, 0.5, 5.0, 0.05, 5);
RNA_def_property_ui_text(prop, "Size", "Particle size in simulation cells");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_flow_reset");
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 1bf14f86189..2463f3c409f 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -2804,18 +2804,32 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ static const EnumPropertyItem prop_operand_items[] = {
+ {eBooleanModifierFlag_Object,
+ "OBJECT",
+ 0,
+ "Object",
+ "Use a mesh object as the operand for the Boolean operation"},
+ {eBooleanModifierFlag_Collection,
+ "COLLECTION",
+ 0,
+ "Collection",
+ "Use a collection of mesh objects as the operand for the Boolean operation"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
static const EnumPropertyItem prop_operation_items[] = {
{eBooleanModifierOp_Intersect,
"INTERSECT",
0,
"Intersect",
- "Keep the part of the mesh that intersects with the other selected object"},
- {eBooleanModifierOp_Union, "UNION", 0, "Union", "Combine two meshes in an additive way"},
+ "Keep the part of the mesh that is common between all operands"},
+ {eBooleanModifierOp_Union, "UNION", 0, "Union", "Combine meshes in an additive way"},
{eBooleanModifierOp_Difference,
"DIFFERENCE",
0,
"Difference",
- "Combine two meshes in a subtractive way"},
+ "Combine meshes in a subtractive way"},
{0, NULL, 0, NULL, NULL},
};
@@ -2843,12 +2857,26 @@ static void rna_def_modifier_boolean(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+ prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "collection");
+ RNA_def_property_struct_type(prop, "Collection");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_ui_text(
+ prop, "Collection", "Use mesh objects in this collection for Boolean operation");
+ RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update");
+
prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, prop_operation_items);
RNA_def_property_enum_default(prop, eBooleanModifierOp_Difference);
RNA_def_property_ui_text(prop, "Operation", "");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "operand_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
+ RNA_def_property_enum_items(prop, prop_operand_items);
+ RNA_def_property_ui_text(prop, "Operand Type", "");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "double_threshold", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "double_threshold");
RNA_def_property_range(prop, 0, 1.0f);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index c7f625e2fa5..77200ce7eda 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -1828,8 +1828,19 @@ static void rna_Object_boundbox_get(PointerRNA *ptr, float *values)
}
}
-static bDeformGroup *rna_Object_vgroup_new(Object *ob, Main *bmain, const char *name)
-{
+static bDeformGroup *rna_Object_vgroup_new(Object *ob,
+ Main *bmain,
+ ReportList *reports,
+ const char *name)
+{
+ if (!OB_TYPE_SUPPORT_VGROUP(ob->type)) {
+ const char *ob_type_name = "Unknown";
+ RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name);
+ BKE_reportf(
+ reports, RPT_ERROR, "VertexGroups.new(): is not supported for '%s' objects", ob_type_name);
+ return NULL;
+ }
+
bDeformGroup *defgroup = BKE_object_defgroup_add_name(ob, name);
DEG_relations_tag_update(bmain);
@@ -2489,7 +2500,7 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop)
/* vertex groups */ /* add_vertex_group */
func = RNA_def_function(srna, "new", "rna_Object_vgroup_new");
- RNA_def_function_flag(func, FUNC_USE_MAIN);
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Add vertex group to object");
RNA_def_string(func, "name", "Group", 0, "", "Vertex group name"); /* optional */
parm = RNA_def_pointer(func, "group", "VertexGroup", "", "New vertex group");
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 7134b085fe7..9aef0b5a1cb 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -1188,9 +1188,12 @@ static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *prop
* This helps a lot in library override case, especially to detect inserted items in collections.
*/
if (!no_prop_name && (is_valid_for_diffing || do_force_name)) {
- PropertyRNA *nameprop_a = RNA_struct_name_property(propptr_a->type);
- PropertyRNA *nameprop_b = (propptr_b != NULL) ? RNA_struct_name_property(propptr_b->type) :
- NULL;
+ PropertyRNA *nameprop_a = (propptr_a->type != NULL) ?
+ RNA_struct_name_property(propptr_a->type) :
+ NULL;
+ PropertyRNA *nameprop_b = (propptr_b != NULL && propptr_b->type != NULL) ?
+ RNA_struct_name_property(propptr_b->type) :
+ NULL;
int propname_a_len = 0, propname_b_len = 0;
char *propname_a = NULL;
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 13b316a9d5d..a85593cbd90 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -4496,7 +4496,7 @@ static void rna_def_space_properties(BlenderRNA *brna)
/* Property search. */
prop = RNA_def_property(srna, "search_filter", PROP_STRING, PROP_NONE);
- RNA_def_property_string_sdna(prop, NULL, "runtime.search_string");
+ RNA_def_property_string_sdna(prop, NULL, "runtime->search_string");
RNA_def_property_ui_text(prop, "Display Filter", "Live search filtering string");
RNA_def_property_flag(prop, PROP_TEXTEDIT_UPDATE);
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index ee7ff472c12..8d7a3cddbc5 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -558,6 +558,12 @@ static void rna_WindowManager_print_undo_steps(wmWindowManager *wm)
BKE_undosys_print(wm->undo_stack);
}
+static void rna_WindowManager_tag_script_reload(void)
+{
+ WM_script_tag_reload();
+ WM_main_add_notifier(NC_WINDOW, NULL);
+}
+
static PointerRNA rna_WindoManager_operator_properties_last(const char *idname)
{
wmOperatorType *ot = WM_operatortype_find(idname, true);
@@ -913,6 +919,12 @@ void RNA_api_wm(StructRNA *srna)
RNA_def_function(srna, "print_undo_steps", "rna_WindowManager_print_undo_steps");
+ /* Used by (#SCRIPT_OT_reload). */
+ func = RNA_def_function(srna, "tag_script_reload", "rna_WindowManager_tag_script_reload");
+ RNA_def_function_ui_description(
+ func, "Tag for refreshing the interface after scripts have been reloaded");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+
parm = RNA_def_property(srna, "is_interface_locked", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(
parm,
diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c
index bef7d5d8e4f..cd552d4e1ad 100644
--- a/source/blender/modifiers/intern/MOD_boolean.c
+++ b/source/blender/modifiers/intern/MOD_boolean.c
@@ -28,16 +28,20 @@
#include "BLI_utildefines.h"
#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLT_translation.h"
+#include "DNA_collection_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_global.h" /* only to check G.debug */
#include "BKE_lib_id.h"
@@ -65,6 +69,7 @@
#include "tools/bmesh_boolean.h"
#include "tools/bmesh_intersect.h"
+// #define DEBUG_TIME
#ifdef DEBUG_TIME
# include "PIL_time.h"
# include "PIL_time_utildefines.h"
@@ -77,7 +82,7 @@ static void initData(ModifierData *md)
bmd->double_threshold = 1e-6f;
bmd->operation = eBooleanModifierOp_Difference;
bmd->solver = eBooleanModifierSolver_Exact;
- bmd->flag = 0;
+ bmd->flag = eBooleanModifierFlag_Object;
}
static bool isDisabled(const struct Scene *UNUSED(scene),
@@ -85,13 +90,15 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
bool UNUSED(useRenderParams))
{
BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Collection *col = bmd->collection;
- /* The object type check is only needed here in case we have a placeholder
- * object assigned (because the library containing the mesh is missing).
- *
- * In other cases it should be impossible to have a type mismatch.
- */
- return !bmd->object || bmd->object->type != OB_MESH;
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ return !bmd->object || bmd->object->type != OB_MESH;
+ }
+ if (bmd->flag & eBooleanModifierFlag_Collection) {
+ return !col;
+ }
+ return false;
}
static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData)
@@ -101,23 +108,45 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk,
walk(userData, ob, &bmd->object, IDWALK_CB_NOP);
}
+static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+
+ walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP);
+
+ /* Needed for the object operand to work. */
+ foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData);
+}
+
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
BooleanModifierData *bmd = (BooleanModifierData *)md;
- if (bmd->object != NULL) {
+ if ((bmd->flag & eBooleanModifierFlag_Object) && bmd->object != NULL) {
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
DEG_add_object_relation(ctx->node, bmd->object, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
}
+
+ Collection *col = bmd->collection;
+
+ if ((bmd->flag & eBooleanModifierFlag_Collection) && col != NULL) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
+ if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
+ DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_TRANSFORM, "Boolean Modifier");
+ DEG_add_object_relation(ctx->node, operand_ob, DEG_OB_COMP_GEOMETRY, "Boolean Modifier");
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
/* We need own transformation as well. */
DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier");
}
static Mesh *get_quick_mesh(
- Object *ob_self, Mesh *mesh_self, Object *ob_other, Mesh *mesh_other, int operation)
+ Object *ob_self, Mesh *mesh_self, Object *ob_operand_ob, Mesh *mesh_operand_ob, int operation)
{
Mesh *result = NULL;
- if (mesh_self->totpoly == 0 || mesh_other->totpoly == 0) {
+ if (mesh_self->totpoly == 0 || mesh_operand_ob->totpoly == 0) {
switch (operation) {
case eBooleanModifierOp_Intersect:
result = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
@@ -128,13 +157,13 @@ static Mesh *get_quick_mesh(
result = mesh_self;
}
else {
- BKE_id_copy_ex(NULL, &mesh_other->id, (ID **)&result, LIB_ID_COPY_LOCALIZE);
+ BKE_id_copy_ex(NULL, &mesh_operand_ob->id, (ID **)&result, LIB_ID_COPY_LOCALIZE);
float imat[4][4];
float omat[4][4];
invert_m4_m4(imat, ob_self->obmat);
- mul_m4_m4m4(omat, imat, ob_other->obmat);
+ mul_m4_m4m4(omat, imat, ob_operand_ob->obmat);
const int mverts_len = result->totvert;
MVert *mv = result->mvert;
@@ -168,208 +197,496 @@ static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data))
return BM_elem_flag_test(f, BM_FACE_TAG) ? 1 : 0;
}
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+static bool BMD_error_messages(ModifierData *md, Collection *col)
{
BooleanModifierData *bmd = (BooleanModifierData *)md;
- Mesh *result = mesh;
- Mesh *mesh_other;
+ bool error_returns_result = false;
+
+ const bool operand_collection = (bmd->flag & eBooleanModifierFlag_Collection) != 0;
+ const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+ const bool operation_intersect = bmd->operation == eBooleanModifierOp_Intersect;
- if (bmd->object == NULL) {
- return result;
+#ifndef WITH_GMP
+ /* If compiled without GMP, return a error. */
+ if (use_exact) {
+ BKE_modifier_set_error(md, "Compiled without GMP, using fast solver");
+ error_returns_result = false;
}
+#endif
- Object *other = bmd->object;
- mesh_other = BKE_modifier_get_evaluated_mesh_from_evaluated_object(other, false);
- if (mesh_other) {
- Object *object = ctx->object;
+ /* If intersect is selected using fast solver, return a error. */
+ if (operand_collection && operation_intersect && !use_exact) {
+ BKE_modifier_set_error(md, "Cannot execute, intersect only available using exact solver");
+ error_returns_result = true;
+ }
- /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
- * But for 2.90 better not try to be smart here. */
- BKE_mesh_wrapper_ensure_mdata(mesh_other);
+ /* If the selected collection is empty and using fast solver, return a error. */
+ if (operand_collection) {
+ if (!use_exact && BKE_collection_is_empty(col)) {
+ BKE_modifier_set_error(md, "Cannot execute, fast solver and empty collection");
+ error_returns_result = true;
+ }
- /* when one of objects is empty (has got no faces) we could speed up
- * calculation a bit returning one of objects' derived meshes (or empty one)
- * Returning mesh is depended on modifiers operation (sergey) */
- result = get_quick_mesh(object, mesh, other, mesh_other, bmd->operation);
+ /* If the selected collection contain non mesh objects, return a error. */
+ if (col) {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
+ if (operand_ob->type != OB_MESH) {
+ BKE_modifier_set_error(
+ md, "Cannot execute, the selected collection contains non mesh objects");
+ error_returns_result = true;
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
- if (result == NULL) {
- const bool is_flip = (is_negative_m4(object->obmat) != is_negative_m4(other->obmat));
+ return error_returns_result;
+}
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_other);
+static BMesh *BMD_mesh_bm_create(
+ Mesh *mesh, Object *object, Mesh *mesh_operand_ob, Object *operand_ob, bool *r_is_flip)
+{
+ BMesh *bm;
-#ifdef DEBUG_TIME
- TIMEIT_START(boolean_bmesh);
-#endif
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
+ *r_is_flip = (is_negative_m4(object->obmat) != is_negative_m4(operand_ob->obmat));
- BM_mesh_bm_from_me(bm,
- mesh_other,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh, mesh_operand_ob);
- if (UNLIKELY(is_flip)) {
- const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
- BMIter iter;
- BMFace *efa;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
- }
- }
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
- BM_mesh_bm_from_me(bm,
- mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ BM_mesh_bm_from_me(bm,
+ mesh_operand_ob,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
- /* main bmesh intersection setup */
- {
- /* create tessface & intersect */
- const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
- int tottri;
- BMLoop *(*looptris)[3];
+ if (UNLIKELY(*r_is_flip)) {
+ const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
+ }
+ }
- looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
+ BM_mesh_bm_from_me(bm,
+ mesh,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
- BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+ return bm;
+}
- /* postpone this until after tessellating
- * so we can use the original normals before the vertex are moved */
- {
- BMIter iter;
- int i;
- const int i_verts_end = mesh_other->totvert;
- const int i_faces_end = mesh_other->totpoly;
+static void BMD_mesh_intersection(BMesh *bm,
+ ModifierData *md,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh_operand_ob,
+ Object *object,
+ Object *operand_ob,
+ bool is_flip)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
- float imat[4][4];
- float omat[4][4];
+ /* main bmesh intersection setup */
+ /* create tessface & intersect */
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ int tottri;
+ BMLoop *(*looptris)[3];
- invert_m4_m4(imat, object->obmat);
- mul_m4_m4m4(omat, imat, other->obmat);
+ looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
- BMVert *eve;
- i = 0;
- BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
- mul_m4_v3(omat, eve->co);
- if (++i == i_verts_end) {
- break;
- }
- }
+ BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
- /* we need face normals because of 'BM_face_split_edgenet'
- * we could calculate on the fly too (before calling split). */
- {
- float nmat[3][3];
- copy_m3_m4(nmat, omat);
- invert_m3(nmat);
+ /* postpone this until after tessellating
+ * so we can use the original normals before the vertex are moved */
+ {
+ BMIter iter;
+ int i;
+ const int i_verts_end = mesh_operand_ob->totvert;
+ const int i_faces_end = mesh_operand_ob->totpoly;
- if (UNLIKELY(is_flip)) {
- negate_m3(nmat);
- }
+ float imat[4][4];
+ float omat[4][4];
- const short ob_src_totcol = other->totcol;
- short *material_remap = BLI_array_alloca(material_remap,
- ob_src_totcol ? ob_src_totcol : 1);
+ invert_m4_m4(imat, object->obmat);
+ mul_m4_m4m4(omat, imat, operand_ob->obmat);
- /* Using original (not evaluated) object here since we are writing to it. */
- /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
- BKE_object_material_remap_calc(ctx->object, other, material_remap);
+ BMVert *eve;
+ i = 0;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ mul_m4_v3(omat, eve->co);
+ if (++i == i_verts_end) {
+ break;
+ }
+ }
- BMFace *efa;
- i = 0;
- BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- mul_transposed_m3_v3(nmat, efa->no);
- normalize_v3(efa->no);
+ /* we need face normals because of 'BM_face_split_edgenet'
+ * we could calculate on the fly too (before calling split). */
+ {
+ float nmat[3][3];
+ copy_m3_m4(nmat, omat);
+ invert_m3(nmat);
- /* Temp tag to test which side split faces are from. */
- BM_elem_flag_enable(efa, BM_FACE_TAG);
+ if (UNLIKELY(is_flip)) {
+ negate_m3(nmat);
+ }
- /* remap material */
- if (LIKELY(efa->mat_nr < ob_src_totcol)) {
- efa->mat_nr = material_remap[efa->mat_nr];
- }
+ const short ob_src_totcol = operand_ob->totcol;
+ short *material_remap = BLI_array_alloca(material_remap, ob_src_totcol ? ob_src_totcol : 1);
- if (++i == i_faces_end) {
- break;
- }
- }
- }
- }
+ /* Using original (not evaluated) object here since we are writing to it. */
+ /* XXX Pretty sure comment above is fully wrong now with CoW & co ? */
+ BKE_object_material_remap_calc(ctx->object, operand_ob, material_remap);
- /* not needed, but normals for 'dm' will be invalid,
- * currently this is ok for 'BM_mesh_intersect' */
- // BM_mesh_normals_update(bm);
+ BMFace *efa;
+ i = 0;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ mul_transposed_m3_v3(nmat, efa->no);
+ normalize_v3(efa->no);
- bool use_separate = false;
- bool use_dissolve = true;
- bool use_island_connect = true;
+ /* Temp tag to test which side split faces are from. */
+ BM_elem_flag_enable(efa, BM_FACE_TAG);
- /* change for testing */
- if (G.debug & G_DEBUG) {
- use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
- use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
- use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) ==
- 0;
+ /* remap material */
+ if (LIKELY(efa->mat_nr < ob_src_totcol)) {
+ efa->mat_nr = material_remap[efa->mat_nr];
}
+ if (++i == i_faces_end) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* not needed, but normals for 'dm' will be invalid,
+ * currently this is ok for 'BM_mesh_intersect' */
+ // BM_mesh_normals_update(bm);
+
+ bool use_separate = false;
+ bool use_dissolve = true;
+ bool use_island_connect = true;
+
+ /* change for testing */
+ if (G.debug & G_DEBUG) {
+ use_separate = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_Separate) != 0;
+ use_dissolve = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoDissolve) == 0;
+ use_island_connect = (bmd->bm_flag & eBooleanModifierBMeshFlag_BMesh_NoConnectRegions) == 0;
+ }
+
#ifdef WITH_GMP
- const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
- const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
+ const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+ const bool use_self = (bmd->flag & eBooleanModifierFlag_Self) != 0;
#else
- if (bmd->solver == eBooleanModifierSolver_Exact) {
- BKE_modifier_set_error(md, "Compiled without GMP, using fast solver");
- }
- const bool use_exact = false;
- const bool use_self = false;
+ const bool use_exact = false;
+ const bool use_self = false;
#endif
- if (use_exact) {
- BM_mesh_boolean(
- bm, looptris, tottri, bm_face_isect_pair, NULL, use_self, bmd->operation);
- }
- else {
- BM_mesh_intersect(bm,
- looptris,
- tottri,
- bm_face_isect_pair,
- NULL,
- false,
- use_separate,
- use_dissolve,
- use_island_connect,
- false,
- false,
- bmd->operation,
- bmd->double_threshold);
+ if (use_exact) {
+ BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, 2, use_self, bmd->operation);
+ }
+ else {
+ BM_mesh_intersect(bm,
+ looptris,
+ tottri,
+ bm_face_isect_pair,
+ NULL,
+ false,
+ use_separate,
+ use_dissolve,
+ use_island_connect,
+ false,
+ false,
+ bmd->operation,
+ bmd->double_threshold);
+ }
+ MEM_freeN(looptris);
+}
+
+static int bm_face_isect_nary(BMFace *f, void *user_data)
+{
+ int *shape = (int *)user_data;
+ return shape[BM_elem_index_get(f)];
+}
+
+/* The Exact solver can do all operands of a collection at once. */
+static Mesh *collection_boolean_exact(BooleanModifierData *bmd,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh)
+{
+ int i;
+ Mesh *result = mesh;
+ Collection *col = bmd->collection;
+ int num_shapes = 1;
+ Mesh **meshes = NULL;
+ Object **objects = NULL;
+ BLI_array_declare(meshes);
+ BLI_array_declare(objects);
+ BMAllocTemplate bat;
+ bat.totvert = mesh->totvert;
+ bat.totedge = mesh->totedge;
+ bat.totloop = mesh->totloop;
+ bat.totface = mesh->totpoly;
+ BLI_array_append(meshes, mesh);
+ BLI_array_append(objects, ctx->object);
+ Mesh *col_mesh;
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, ob) {
+ if (ob->type == OB_MESH && ob != ctx->object) {
+ col_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob, false);
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(col_mesh);
+ BLI_array_append(meshes, col_mesh);
+ BLI_array_append(objects, ob);
+ bat.totvert += col_mesh->totvert;
+ bat.totedge += col_mesh->totedge;
+ bat.totloop += col_mesh->totloop;
+ bat.totface += col_mesh->totpoly;
+ ++num_shapes;
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ int *shape_face_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
+ int *shape_vert_end = MEM_mallocN(num_shapes * sizeof(int), __func__);
+ bool is_neg_mat0 = is_negative_m4(ctx->object->obmat);
+ BMesh *bm = BM_mesh_create(&bat,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+ for (i = 0; i < num_shapes; i++) {
+ Mesh *me = meshes[i];
+ Object *ob = objects[i];
+ /* Need normals for triangulation. */
+ BM_mesh_bm_from_me(bm,
+ me,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ shape_face_end[i] = me->totpoly + (i == 0 ? 0 : shape_face_end[i - 1]);
+ shape_vert_end[i] = me->totvert + (i == 0 ? 0 : shape_vert_end[i - 1]);
+ if (i > 0) {
+ bool is_flip = (is_neg_mat0 != is_negative_m4(ob->obmat));
+ if (UNLIKELY(is_flip)) {
+ const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
+ BMIter iter;
+ BMFace *efa;
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_index_get(efa) >= shape_face_end[i - 1]) {
+ BM_face_normal_flip_ex(bm, efa, cd_loop_mdisp_offset, true);
+ }
}
+ }
+ }
+ }
- MEM_freeN(looptris);
+ /* Triangulate the mesh. */
+ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
+ int tottri;
+ BMLoop *(*looptris)[3];
+ looptris = MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__);
+ BM_mesh_calc_tessellation_beauty(bm, looptris, &tottri);
+
+ /* Move the vertices of all but the first shape into transformation space of first mesh.
+ * Do this after tesselation so don't need to recalculate normals.
+ * The Exact solver doesn't need normals on the input faces. */
+ float imat[4][4];
+ float omat[4][4];
+ invert_m4_m4(imat, ctx->object->obmat);
+ int curshape = 0;
+ int curshape_vert_end = shape_vert_end[0];
+ BMVert *eve;
+ BMIter iter;
+ i = 0;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (i == curshape_vert_end) {
+ curshape++;
+ curshape_vert_end = shape_vert_end[curshape];
+ mul_m4_m4m4(omat, imat, objects[curshape]->obmat);
+ }
+ if (curshape > 0) {
+ mul_m4_v3(omat, eve->co);
+ }
+ i++;
+ }
+
+ /* Remap the materials. Fill a shape array for test function. Calculate normals. */
+ int *shape = MEM_mallocN(bm->totface * sizeof(int), __func__);
+ curshape = 0;
+ int curshape_face_end = shape_face_end[0];
+ int curshape_ncol = ctx->object->totcol;
+ short *material_remap = NULL;
+ BMFace *efa;
+ i = 0;
+ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
+ if (i == curshape_face_end) {
+ curshape++;
+ curshape_face_end = shape_face_end[curshape];
+ if (material_remap != NULL) {
+ MEM_freeN(material_remap);
}
+ curshape_ncol = objects[curshape]->totcol;
+ material_remap = MEM_mallocN(curshape_ncol ? curshape_ncol : 1, __func__);
+ BKE_object_material_remap_calc(ctx->object, objects[curshape], material_remap);
+ }
+ shape[i] = curshape;
+ if (curshape > 0) {
+ /* Normals for other shapes changed because vertex positions changed.
+ * Boolean doesn't need these, but post-boolean code (interpolation) does. */
+ BM_face_normal_update(efa);
+ if (LIKELY(efa->mat_nr < curshape_ncol)) {
+ efa->mat_nr = material_remap[efa->mat_nr];
+ }
+ }
+ i++;
+ }
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ BM_mesh_boolean(
+ bm, looptris, tottri, bm_face_isect_nary, shape, num_shapes, true, bmd->operation);
- BM_mesh_free(bm);
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
- result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ MEM_freeN(shape);
+ MEM_freeN(shape_face_end);
+ MEM_freeN(shape_vert_end);
+ MEM_freeN(looptris);
+ if (material_remap != NULL) {
+ MEM_freeN(material_remap);
+ }
+ BLI_array_free(meshes);
+ BLI_array_free(objects);
+ return result;
+}
+
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
+{
+ BooleanModifierData *bmd = (BooleanModifierData *)md;
+ Object *object = ctx->object;
+ Mesh *result = mesh;
+ Mesh *mesh_operand_ob;
+ BMesh *bm;
+ Collection *col = bmd->collection;
+
+ bool is_flip = false;
+ const bool confirm_return = true;
+#ifdef WITH_GMP
+ const bool use_exact = bmd->solver == eBooleanModifierSolver_Exact;
+#else
+ const bool use_exact = false;
+#endif
#ifdef DEBUG_TIME
- TIMEIT_END(boolean_bmesh);
+ TIMEIT_START(boolean_bmesh);
#endif
+
+ if (bmd->flag & eBooleanModifierFlag_Object) {
+ if (bmd->object == NULL) {
+ return result;
}
- /* if new mesh returned, return it; otherwise there was
- * an error, so delete the modifier object */
- if (result == NULL) {
- BKE_modifier_set_error(md, "Cannot execute boolean operation");
+ BMD_error_messages(md, NULL);
+
+ Object *operand_ob = bmd->object;
+
+ mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob, false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+ /* when one of objects is empty (has got no faces) we could speed up
+ * calculation a bit returning one of objects' derived meshes (or empty one)
+ * Returning mesh is depended on modifiers operation (sergey) */
+ result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
+
+ if (result == NULL) {
+ bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+
+ /* if new mesh returned, return it; otherwise there was
+ * an error, so delete the modifier object */
+ if (result == NULL) {
+ BKE_modifier_set_error(md, "Cannot execute boolean operation");
+ }
}
}
+ else {
+ if (col == NULL && !use_exact) {
+ return result;
+ }
+
+ /* Return result for certain errors. */
+ if (BMD_error_messages(md, col) == confirm_return) {
+ return result;
+ }
+
+ if (use_exact) {
+ result = collection_boolean_exact(bmd, ctx, mesh);
+ }
+ else {
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (col, operand_ob) {
+ if (operand_ob->type == OB_MESH && operand_ob != ctx->object) {
+
+ mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
+ false);
+
+ if (mesh_operand_ob) {
+ /* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
+ * But for 2.90 better not try to be smart here. */
+ BKE_mesh_wrapper_ensure_mdata(mesh_operand_ob);
+ /* when one of objects is empty (has got no faces) we could speed up
+ * calculation a bit returning one of objects' derived meshes (or empty one)
+ * Returning mesh is depended on modifiers operation (sergey) */
+ result = get_quick_mesh(object, mesh, operand_ob, mesh_operand_ob, bmd->operation);
+
+ if (result == NULL) {
+ bm = BMD_mesh_bm_create(mesh, object, mesh_operand_ob, operand_ob, &is_flip);
+
+ BMD_mesh_intersection(bm, md, ctx, mesh_operand_ob, object, operand_ob, is_flip);
+
+ /* Needed for multiple objects to work. */
+ BM_mesh_bm_to_me(NULL,
+ bm,
+ mesh,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = false,
+ }));
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ }
+ /* if new mesh returned, return it; otherwise there was
+ * an error, so delete the modifier object */
+ if (result == NULL) {
+ BKE_modifier_set_error(md, "Cannot execute boolean operation");
+ }
+ }
+ }
+ }
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ }
+ }
+
+#ifdef DEBUG_TIME
+ TIMEIT_END(boolean_bmesh);
+#endif
+
return result;
}
@@ -392,13 +709,26 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
+ uiItemR(layout, ptr, "operand_type", 0, NULL, ICON_NONE);
+
+ const bool operand_object = RNA_enum_get(ptr, "operand_type") == eBooleanModifierFlag_Object;
+
+ if (operand_object) {
+ uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "collection", 0, NULL, ICON_NONE);
+ }
+
const bool use_exact = RNA_enum_get(ptr, "solver") == eBooleanModifierSolver_Exact;
- uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
if (use_exact) {
- uiItemR(layout, ptr, "use_self", 0, NULL, ICON_NONE);
+ /* When operand is collection, we always use_self. */
+ if (operand_object) {
+ uiItemR(layout, ptr, "use_self", 0, NULL, ICON_NONE);
+ }
}
else {
uiItemR(layout, ptr, "double_threshold", 0, NULL, ICON_NONE);
@@ -443,7 +773,7 @@ ModifierTypeInfo modifierType_Boolean = {
/* dependsOnTime */ NULL,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ foreachObjectLink,
- /* foreachIDLink */ NULL,
+ /* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
/* panelRegister */ panelRegister,
diff --git a/source/blender/modifiers/intern/MOD_meshcache_pc2.c b/source/blender/modifiers/intern/MOD_meshcache_pc2.c
index 120d1d6d71a..0ef9f26f1d7 100644
--- a/source/blender/modifiers/intern/MOD_meshcache_pc2.c
+++ b/source/blender/modifiers/intern/MOD_meshcache_pc2.c
@@ -81,7 +81,7 @@ static bool meshcache_read_pc2_head(FILE *fp,
}
/**
- * Gets the index frange and factor
+ * Gets the index range and factor
*
* currently same as for MDD
*/
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 0980d9df762..2a773519cb6 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -890,22 +890,36 @@ static PyObject *pyrna_struct_str(BPy_StructRNA *self)
{
PyObject *ret;
const char *name;
+ const char *extra_info = "";
if (!PYRNA_STRUCT_IS_VALID(self)) {
return PyUnicode_FromFormat("<bpy_struct, %.200s invalid>", Py_TYPE(self)->tp_name);
}
- /* Print name if available. */
+ ID *id = self->ptr.owner_id;
+ if (id && id != DEG_get_original_id(id)) {
+ extra_info = ", evaluated";
+ }
+
+ /* Print name if available.
+ *
+ * Always include the pointer address since it can help identify unique data,
+ * or when data is re-allocated internally. */
name = RNA_struct_name_get_alloc(&self->ptr, NULL, 0, NULL);
if (name) {
- ret = PyUnicode_FromFormat(
- "<bpy_struct, %.200s(\"%.200s\")>", RNA_struct_identifier(self->ptr.type), name);
+ ret = PyUnicode_FromFormat("<bpy_struct, %.200s(\"%.200s\") at %p%s>",
+ RNA_struct_identifier(self->ptr.type),
+ name,
+ self->ptr.data,
+ extra_info);
MEM_freeN((void *)name);
return ret;
}
- return PyUnicode_FromFormat(
- "<bpy_struct, %.200s at %p>", RNA_struct_identifier(self->ptr.type), self->ptr.data);
+ return PyUnicode_FromFormat("<bpy_struct, %.200s at %p%s>",
+ RNA_struct_identifier(self->ptr.type),
+ self->ptr.data,
+ extra_info);
}
static PyObject *pyrna_struct_repr(BPy_StructRNA *self)
@@ -914,18 +928,14 @@ static PyObject *pyrna_struct_repr(BPy_StructRNA *self)
PyObject *tmp_str;
PyObject *ret;
- if (id == NULL || !PYRNA_STRUCT_IS_VALID(self)) {
+ if (id == NULL || !PYRNA_STRUCT_IS_VALID(self) || (DEG_get_original_id(id) != id)) {
/* fallback */
return pyrna_struct_str(self);
}
tmp_str = PyUnicode_FromString(id->name + 2);
- if (DEG_get_original_id(id) != id) {
- ret = PyUnicode_FromFormat(
- "Evaluated %s %R", BKE_idtype_idcode_to_name(GS(id->name)), tmp_str);
- }
- else if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) {
+ if (RNA_struct_is_ID(self->ptr.type) && (id->flag & LIB_EMBEDDED_DATA) == 0) {
ret = PyUnicode_FromFormat(
"bpy.data.%s[%R]", BKE_idtype_idcode_to_name_plural(GS(id->name)), tmp_str);
}
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 74a94e997e0..a38cbb920c4 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -618,8 +618,9 @@ int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
-static void gesture_lasso_apply(bContext *C, wmOperator *op)
+static int gesture_lasso_apply(bContext *C, wmOperator *op)
{
+ int retval = OPERATOR_FINISHED;
wmGesture *gesture = op->customdata;
PointerRNA itemptr;
float loc[2];
@@ -639,9 +640,11 @@ static void gesture_lasso_apply(bContext *C, wmOperator *op)
gesture_modal_end(C, op);
if (op->type->exec) {
- int retval = op->type->exec(C, op);
+ retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval);
}
+
+ return retval;
}
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
@@ -683,8 +686,7 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
case MIDDLEMOUSE:
case RIGHTMOUSE:
if (event->val == KM_RELEASE) { /* key release */
- gesture_lasso_apply(C, op);
- return OPERATOR_FINISHED;
+ return gesture_lasso_apply(C, op);
}
break;
case EVT_ESCKEY:
diff --git a/tests/python/alembic_export_tests.py b/tests/python/alembic_export_tests.py
index b5d6bf65f3f..1d34eb3fc81 100644
--- a/tests/python/alembic_export_tests.py
+++ b/tests/python/alembic_export_tests.py
@@ -111,6 +111,7 @@ class AbstractAlembicTest(AbstractBlenderRunnerTest):
'uint64_t': int,
'float64_t': float,
'float32_t': float,
+ 'string': str,
}
result = {}
@@ -586,6 +587,69 @@ class InvisibleObjectExportTest(AbstractAlembicTest):
test('InvisibleAnimatedCube', False)
+class CustomPropertiesExportTest(AbstractAlembicTest):
+ """Test export of custom properties."""
+
+ def _run_export(self, tempdir: pathlib.Path) -> pathlib.Path:
+ abc = tempdir / 'custom-properties.abc'
+ script = "import bpy; bpy.context.scene.frame_set(1); bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1)" % abc.as_posix()
+ self.run_blender('custom-properties.blend', script)
+ return abc
+
+ @with_tempdir
+ def test_xform_props(self, tempdir: pathlib.Path) -> None:
+ abc = self._run_export(tempdir)
+ abcprop = self.abcprop(abc, '/Cube/.xform/.userProperties')
+
+ # Simple, single values.
+ self.assertEqual(abcprop['static_int'], [327])
+ self.assertEqual(abcprop['static_float'], [47.01])
+ self.assertEqual(abcprop['static_string'], ['Agents'])
+ self.assertEqual(abcprop['keyed_float'], [-1])
+ self.assertEqual(abcprop['keyed_int'], [-47])
+
+ # Arrays.
+ self.assertEqual(abcprop['keyed_array_float'], [-1.000, 0.000, 1.000])
+ self.assertEqual(abcprop['keyed_array_int'], [42, 47, 327])
+
+ # Multi-dimensional arrays.
+ self.assertEqual(abcprop['array_of_strings'], ['ผัดไทย', 'Pad Thai'])
+ self.assertEqual(
+ abcprop['matrix_tuple'],
+ [1.0, 0.0, 0.0, 3.33333, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
+ self.assertEqual(
+ abcprop['static_matrix'],
+ [1.0, 0.0, 0.0, 3.33333, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0])
+ self.assertEqual(
+ abcprop['nonuniform_array'],
+ [10, 20, 30, 1, 2, 47])
+
+ @with_tempdir
+ def test_mesh_props(self, tempdir: pathlib.Path) -> None:
+ abc = self._run_export(tempdir)
+ abcprop = self.abcprop(abc, '/Cube/Cube/.geom/.userProperties')
+ self.assertEqual(abcprop['mesh_tags'], ['cube', 'box', 'low-poly-sphere'])
+
+ @with_tempdir
+ def test_camera_props(self, tempdir: pathlib.Path) -> None:
+ abc = self._run_export(tempdir)
+ abcprop = self.abcprop(abc, '/Camera/Hasselblad/.geom/.userProperties')
+ self.assertEqual(abcprop['type'], ['500c/m'])
+
+ @with_tempdir
+ def test_disabled_export_option(self, tempdir: pathlib.Path) -> None:
+ abc = tempdir / 'custom-properties.abc'
+ script = (
+ "import bpy; bpy.context.scene.frame_set(1); "
+ "bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1, export_custom_properties=False)" % abc.as_posix()
+ )
+ self.run_blender('custom-properties.blend', script)
+
+ abcprop = self.abcprop(abc, '/Camera/Hasselblad/.geom/.userProperties')
+ self.assertIn('eyeSeparation', abcprop, 'Regular non-standard properties should still be written')
+ self.assertNotIn('type', abcprop, 'Custom properties should not be written')
+
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--blender', required=True)