From 809b33b69a04fc37e1535697b667158ca80f6d60 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Sep 2021 15:29:33 +1000 Subject: Docs: improve the error when undo poll fails Calling undo in from Python background-mode would raise an exception without any information about how to initialize undo. --- source/blender/editors/undo/ed_undo.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 3e0029156c1..84d5d3b9aae 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -574,7 +574,12 @@ static bool ed_undo_is_init_poll(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); if (wm->undo_stack == NULL) { - CTX_wm_operator_poll_msg_set(C, "Undo disabled at startup"); + /* This message is intended for Python developers, + * it will be part of the exception when attempting to call undo in background mode. */ + CTX_wm_operator_poll_msg_set( + C, + "Undo disabled at startup in background-mode. " + "Call `ed.undo_push()` to explicitly initialize the undo-system."); return false; } return true; -- cgit v1.2.3 From 0708733c46cb6b50697c1b80a6541d6fb410267e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Sep 2021 15:29:34 +1000 Subject: Fix T91123: Freeing meshes in edit-mode leaks memory Freeing the edit-mesh pointer wasn't free the edit-mesh data it's self. Unlinking from the outliner or through the Python API leaked memory. This didn't often cause leaks in practice since ED_editors_exit exits edit-mode for all objects. Now freeing mesh data frees the edit-mode data as well, this matches the behavior of other object types that support edit-mode. --- source/blender/blenkernel/intern/editmesh.c | 8 ++++++-- source/blender/blenkernel/intern/mesh.c | 6 ++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 83e03ef44f5..a925dfa4da8 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -211,10 +211,14 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, void BKE_editmesh_free_derived_caches(BMEditMesh *em) { if (em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_cage); + Mesh *me = em->mesh_eval_cage; + MEM_SAFE_FREE(me->edit_mesh); + BKE_id_free(NULL, me); } if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) { - BKE_id_free(NULL, em->mesh_eval_final); + Mesh *me = em->mesh_eval_final; + MEM_SAFE_FREE(me->edit_mesh); + BKE_id_free(NULL, me); } em->mesh_eval_cage = em->mesh_eval_final = NULL; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index bfc05c2d624..daff82802f9 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -160,6 +160,12 @@ static void mesh_free_data(ID *id) BLI_freelistN(&mesh->vertex_group_names); + if (mesh->edit_mesh) { + BKE_editmesh_free_data(mesh->edit_mesh); + MEM_freeN(mesh->edit_mesh); + mesh->edit_mesh = NULL; + } + BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(mesh); MEM_SAFE_FREE(mesh->mat); -- cgit v1.2.3 From 19e1b5c1fd61b622382c2cf7d782e99f6cb8c64b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Sep 2021 15:29:36 +1000 Subject: Fix T90972: Crash calling Mesh.clear_geometry in edit-mode No longer free the edit-mesh pointer while in edit-mode since this isn't reliable to keep the object in edit-mode while freeing it's edit-mesh data. Users who want to exit edit-mode should do so explicitly. Caused by 6d2f9b1dfa98502e9e9f1a73e3dded2ded1f1ce6. --- source/blender/blenkernel/intern/mesh.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index daff82802f9..6814083817c 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -886,6 +886,18 @@ void BKE_mesh_free_data_for_undo(Mesh *me) mesh_free_data(&me->id); } +/** + * \note on data that this function intentionally doesn't free: + * + * - Materials and shape keys are not freed here (#Mesh.mat & #Mesh.key). + * As freeing shape keys requires tagging the depsgraph for updated relations, + * which is expensive. + * Material slots should be kept in sync with the object. + * + * - Edit-Mesh (#Mesh.edit_mesh) + * Since edit-mesh is tied to the objects mode, + * which crashes when called in edit-mode, see: T90972. + */ static void mesh_clear_geometry(Mesh *mesh) { CustomData_free(&mesh->vdata, mesh->totvert); @@ -895,11 +907,6 @@ static void mesh_clear_geometry(Mesh *mesh) CustomData_free(&mesh->pdata, mesh->totpoly); MEM_SAFE_FREE(mesh->mselect); - MEM_SAFE_FREE(mesh->edit_mesh); - - /* Note that materials and shape keys are not freed here. This is intentional, as freeing - * shape keys requires tagging the depsgraph for updated relations, which is expensive. - * Material slots should be kept in sync with the object. */ mesh->totvert = 0; mesh->totedge = 0; -- cgit v1.2.3 From a2f3aca6470b9a7f211ff95c5f3efeb7f62cb9f4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Sep 2021 15:29:37 +1000 Subject: Cleanup: remove redundant edit-mesh memory allocation This memory was only duplicated to satisfy mesh_data_free which was incorrectly freeing the pointer (but nothing else). --- source/blender/blenkernel/intern/editmesh.c | 4 ++-- source/blender/blenkernel/intern/mesh_wrapper.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index a925dfa4da8..e178b0c42d0 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -212,12 +212,12 @@ void BKE_editmesh_free_derived_caches(BMEditMesh *em) { if (em->mesh_eval_cage) { Mesh *me = em->mesh_eval_cage; - MEM_SAFE_FREE(me->edit_mesh); + me->edit_mesh = NULL; BKE_id_free(NULL, me); } if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) { Mesh *me = em->mesh_eval_final; - MEM_SAFE_FREE(me->edit_mesh); + me->edit_mesh = NULL; BKE_id_free(NULL, me); } em->mesh_eval_cage = em->mesh_eval_final = NULL; diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index fe6af432314..de7259757bf 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -69,7 +69,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, /* Use edit-mesh directly where possible. */ me->runtime.is_original = true; - me->edit_mesh = MEM_dupallocN(em); + me->edit_mesh = em; /* Make sure, we crash if these are ever used. */ #ifdef DEBUG -- cgit v1.2.3 From a8739ae6c2acbf0737113fbebf7e5ac0a942aaee Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 2 Sep 2021 15:39:52 +1000 Subject: Fix regression in recent change 0708733c46cb6b50697c1b80a6541d6fb410267e Adding a mirror modifier in edit-mode crashed. Freeing meshes that hold a shallow copy happens in multiple places while calculating modifiers, making it impractical to clear the edit-mode pointer before freeing the mesh (as done in BKE_editmesh_free_derived_caches). Add a struct member to the edit-mesh struct so evaluated copies don't free the edit-mesh contents. --- source/blender/blenkernel/BKE_editmesh.h | 5 +++++ source/blender/blenkernel/intern/editmesh.c | 8 ++------ source/blender/blenkernel/intern/mesh.c | 4 +++- source/blender/blenkernel/intern/mesh_wrapper.c | 4 +++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index ffd8ac42c63..2c24b1a5487 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -78,6 +78,11 @@ typedef struct BMEditMesh { /** Temp variables for x-mirror editing (-1 when the layer does not exist). */ int mirror_cdlayer; + /** + * Enable for evaluated copies, causes the edit-mesh to free the memory, not it's contents. + */ + char is_shallow_copy; + /** * ID data is older than edit-mode data. * Set #Main.is_memfile_undo_flush_needed when enabling. diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index e178b0c42d0..83e03ef44f5 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -211,14 +211,10 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, void BKE_editmesh_free_derived_caches(BMEditMesh *em) { if (em->mesh_eval_cage) { - Mesh *me = em->mesh_eval_cage; - me->edit_mesh = NULL; - BKE_id_free(NULL, me); + BKE_id_free(NULL, em->mesh_eval_cage); } if (em->mesh_eval_final && em->mesh_eval_final != em->mesh_eval_cage) { - Mesh *me = em->mesh_eval_final; - me->edit_mesh = NULL; - BKE_id_free(NULL, me); + BKE_id_free(NULL, em->mesh_eval_final); } em->mesh_eval_cage = em->mesh_eval_final = NULL; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 6814083817c..2efe0d77d87 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -161,7 +161,9 @@ static void mesh_free_data(ID *id) BLI_freelistN(&mesh->vertex_group_names); if (mesh->edit_mesh) { - BKE_editmesh_free_data(mesh->edit_mesh); + if (mesh->edit_mesh->is_shallow_copy == false) { + BKE_editmesh_free_data(mesh->edit_mesh); + } MEM_freeN(mesh->edit_mesh); mesh->edit_mesh = NULL; } diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index de7259757bf..bc1ffeb8cf4 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -69,7 +69,9 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em, /* Use edit-mesh directly where possible. */ me->runtime.is_original = true; - me->edit_mesh = em; + + me->edit_mesh = MEM_dupallocN(em); + me->edit_mesh->is_shallow_copy = true; /* Make sure, we crash if these are ever used. */ #ifdef DEBUG -- cgit v1.2.3 From 799a2b07ad13b879d1148b8ffe8ccd8e7e421212 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 1 Sep 2021 17:43:20 +0200 Subject: Fix possible missing render result with `update_result` Need to ensure render result's pixels are allocated prior to merge. Differential Revision: https://developer.blender.org/D12371 --- source/blender/render/intern/engine.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 481a6662cc0..5728b784714 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -365,6 +365,17 @@ RenderResult *RE_engine_begin_result( return result; } +static void re_ensure_passes_allocated_thread_safe(Render *re) +{ + if (!re->result->passes_allocated) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if (!re->result->passes_allocated) { + render_result_passes_allocated_ensure(re->result); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + void RE_engine_update_result(RenderEngine *engine, RenderResult *result) { if (engine->bake.pixels) { @@ -375,6 +386,7 @@ void RE_engine_update_result(RenderEngine *engine, RenderResult *result) Render *re = engine->re; if (result) { + re_ensure_passes_allocated_thread_safe(re); render_result_merge(re->result, result); result->renlay = result->layers.first; /* weak, draws first layer always */ re->display_update(re->duh, result, NULL); @@ -412,13 +424,7 @@ void RE_engine_end_result( return; } - if (!re->result->passes_allocated) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (!re->result->passes_allocated) { - render_result_passes_allocated_ensure(re->result); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } + re_ensure_passes_allocated_thread_safe(re); /* merge. on break, don't merge in result for preview renders, looks nicer */ if (!highlight) { -- cgit v1.2.3 From 546314fc9669f88dd501b50be159fa0219813596 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 2 Sep 2021 12:11:32 +0200 Subject: Build utils: `make_update`: Add option to choose SVN branch. Needed for studio sprite-fright frozen branch. Also do not overwrite branch for git sub-modules when it is defined, and fallback to `master` branch in case specified branch is not found in a specific sub-repository. --- build_files/utils/make_update.py | 28 ++++++++++++++++++---------- build_files/utils/make_utils.py | 4 +++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index 2b8c7af98fb..b901fa56f52 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -31,6 +31,7 @@ def parse_arguments(): parser.add_argument("--no-submodules", action="store_true") parser.add_argument("--use-tests", action="store_true") parser.add_argument("--svn-command", default="svn") + parser.add_argument("--svn-branch", default=None) parser.add_argument("--git-command", default="git") parser.add_argument("--use-centos-libraries", action="store_true") return parser.parse_args() @@ -46,7 +47,7 @@ def svn_update(args, release_version): svn_non_interactive = [args.svn_command, '--non-interactive'] lib_dirpath = os.path.join(get_blender_git_root(), '..', 'lib') - svn_url = make_utils.svn_libraries_base_url(release_version) + svn_url = make_utils.svn_libraries_base_url(release_version, args.svn_branch) # Checkout precompiled libraries if sys.platform == 'darwin': @@ -170,26 +171,28 @@ def submodules_update(args, release_version, branch): sys.stderr.write("git not found, can't update code\n") sys.exit(1) - # Update submodules to latest master or appropriate release branch. - if not release_version: - branch = "master" + # Update submodules to appropriate given branch, + # falling back to master if none is given and/or found in a sub-repository. + branch_fallback = "master" + if not branch: + branch = branch_fallback submodules = [ - ("release/scripts/addons", branch), - ("release/scripts/addons_contrib", branch), - ("release/datafiles/locale", branch), - ("source/tools", branch), + ("release/scripts/addons", branch, branch_fallback), + ("release/scripts/addons_contrib", branch, branch_fallback), + ("release/datafiles/locale", branch, branch_fallback), + ("source/tools", branch, branch_fallback), ] # Initialize submodules only if needed. - for submodule_path, submodule_branch in submodules: + for submodule_path, submodule_branch, submodule_branch_fallback in submodules: if not os.path.exists(os.path.join(submodule_path, ".git")): call([args.git_command, "submodule", "update", "--init", "--recursive"]) break # Checkout appropriate branch and pull changes. skip_msg = "" - for submodule_path, submodule_branch in submodules: + for submodule_path, submodule_branch, submodule_branch_fallback in submodules: cwd = os.getcwd() try: os.chdir(submodule_path) @@ -201,6 +204,11 @@ def submodules_update(args, release_version, branch): call([args.git_command, "fetch", "origin"]) call([args.git_command, "checkout", submodule_branch]) call([args.git_command, "pull", "--rebase", "origin", submodule_branch]) + # If we cannot find the specified branch for this submodule, fallback to default one (aka master). + if make_utils.git_branch(args.git_command) != submodule_branch: + call([args.git_command, "fetch", "origin"]) + call([args.git_command, "checkout", submodule_branch_fallback]) + call([args.git_command, "pull", "--rebase", "origin", submodule_branch_fallback]) finally: os.chdir(cwd) diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py index 9f928bb524d..7cd23baad68 100755 --- a/build_files/utils/make_utils.py +++ b/build_files/utils/make_utils.py @@ -70,9 +70,11 @@ def git_branch_release_version(branch, tag): return release_version -def svn_libraries_base_url(release_version): +def svn_libraries_base_url(release_version, branch): if release_version: svn_branch = "tags/blender-" + release_version + "-release" + elif branch: + svn_branch = "branches/" + branch else: svn_branch = "trunk" return "https://svn.blender.org/svnroot/bf-blender/" + svn_branch + "/lib/" -- cgit v1.2.3 From 8849bed67199f9f0ceb6ea6c92feec2281bbb9af Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 2 Sep 2021 10:18:44 -0300 Subject: Revert "PyAPI: GPU Shader: add 'state' parameter to uniform sampler" This reverts commit 2aad8fc7bc2a45d5f749430c7cb9b82b9f6c9e49. It was a commit without proper review. A better API needs to be discussed. --- source/blender/python/gpu/gpu_py_shader.c | 59 +++++-------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index d2167f2f102..95e505b1343 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -76,21 +76,6 @@ static const struct PyC_StringEnumItems pygpu_shader_config_items[] = { {0, NULL}, }; -static const struct PyC_FlagSet pygpu_texture_samplerstate_items[] = { - {GPU_SAMPLER_DEFAULT, "DEFAULT"}, - {GPU_SAMPLER_FILTER, "FILTER"}, - {GPU_SAMPLER_MIPMAP, "MIPMAP"}, - {GPU_SAMPLER_REPEAT_S, "REPEAT_S"}, - {GPU_SAMPLER_REPEAT_T, "REPEAT_T"}, - {GPU_SAMPLER_REPEAT_R, "REPEAT_R"}, - {GPU_SAMPLER_CLAMP_BORDER, "CLAMP_BORDER"}, - {GPU_SAMPLER_COMPARE, "COMPARE"}, - {GPU_SAMPLER_ANISO, "ANISO"}, - {GPU_SAMPLER_ICON, "ICON"}, - {GPU_SAMPLER_REPEAT, "REPEAT"}, - {0, NULL}, -}; - static int pygpu_shader_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix) @@ -507,53 +492,25 @@ static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) } PyDoc_STRVAR(pygpu_shader_uniform_sampler_doc, - ".. method:: uniform_sampler(name, texture, state={'DEFAULT'})\n" + ".. method:: uniform_sampler(name, texture)\n" "\n" - " Specify the texture and state for an uniform sampler in the current GPUShader.\n" + " Specify the value of a texture uniform variable for the current GPUShader.\n" "\n" " :param name: name of the uniform variable whose texture is to be specified.\n" " :type name: str\n" " :param texture: Texture to attach.\n" - " :type texture: :class:`gpu.types.GPUTexture`\n" - " :param state: set of values in:\n" - "\n" - " - ``DEFAULT``\n" - " - ``FILTER``\n" - " - ``MIPMAP``\n" - " - ``REPEAT_S``\n" - " - ``REPEAT_T``\n" - " - ``REPEAT_R``\n" - " - ``CLAMP_BORDER``\n" - " - ``COMPARE``\n" - " - ``ANISO``\n" - " - ``ICON``\n" - " - ``REPEAT``\n" - " :type state: set\n"); -static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args, PyObject *kwds) + " :type texture: :class:`gpu.types.GPUTexture`\n"); +static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args) { const char *name; BPyGPUTexture *py_texture; - PyObject *py_samplerstate = NULL; - - static const char *_keywords[] = {"name", "texture", "state", NULL}; - static _PyArg_Parser _parser = {"sO!|$O:uniform_sampler", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kwds, &_parser, &name, &BPyGPUTexture_Type, &py_texture, &py_samplerstate)) { + if (!PyArg_ParseTuple( + args, "sO!:GPUShader.uniform_sampler", &name, &BPyGPUTexture_Type, &py_texture)) { return NULL; } - int sampler_state = GPU_SAMPLER_DEFAULT; - if (py_samplerstate) { - if (PyC_FlagSet_ToBitfield(pygpu_texture_samplerstate_items, - py_samplerstate, - &sampler_state, - "shader.uniform_sampler") == -1) { - return NULL; - } - } - int slot = GPU_shader_get_texture_binding(self->shader, name); - GPU_texture_bind_ex(py_texture->tex, (eGPUSamplerState)sampler_state, slot, false); + GPU_texture_bind(py_texture->tex, slot); GPU_shader_uniform_1i(self->shader, name, slot); Py_RETURN_NONE; @@ -665,7 +622,7 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = { pygpu_shader_uniform_int_doc}, {"uniform_sampler", (PyCFunction)pygpu_shader_uniform_sampler, - METH_VARARGS | METH_KEYWORDS, + METH_VARARGS, pygpu_shader_uniform_sampler_doc}, {"uniform_block", (PyCFunction)pygpu_shader_uniform_block, -- cgit v1.2.3 From 6fc92b296fae474c5b9bb2ea996f51cef75bd9c6 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 2 Sep 2021 16:23:13 +0200 Subject: GPencil: Change default Scale Thickness to True in 2D template This parameter is more logic as true because is better scale thickness when the size of the stroke changes. Reviewed by: @mendio @pepeland --- release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py index be47890a002..c8328f5ee42 100644 --- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py +++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py @@ -46,6 +46,7 @@ def update_factory_startup_screens(): def update_factory_startup_scenes(): for scene in bpy.data.scenes: scene.tool_settings.use_keyframe_insert_auto = True + scene.tool_settings.gpencil_sculpt.use_scale_thickness = True def update_factory_startup_grease_pencils(): -- cgit v1.2.3 From 4a3243f311c75d8b15fc39ec07dd57ecb40bd32f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Sep 2021 14:37:36 +1000 Subject: Docs: add note to skip_fcurve_selected_data sequence strip check Without an explanation the sequencer logic looked wrong since other selection checks don't skip data that can't be found. --- source/blender/editors/animation/anim_filter.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 7e3e3f363c2..104540e2257 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1111,7 +1111,27 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id /* Can only add this F-Curve if it is selected. */ if (ads->filterflag & ADS_FILTER_ONLYSEL) { - if ((seq == NULL) || (seq->flag & SELECT) == 0) { + + /* NOTE(@campbellbarton): The `seq == NULL` check doesn't look right + * (compared to other checks in this function which skip data that can't be found). + * + * This is done since the search for sequence strips doesn't use a global lookup: + * - Nested meta-strips are excluded. + * - When inside a meta-strip - strips outside the meta-strip excluded. + * + * Instead, only the strips directly visible to the user are considered for selection. + * The NULL check here means everything else is considered unselected and is not shown. + * + * There is a subtle difference between nodes, pose-bones ... etc + * since data-paths that point to missing strips are not shown. + * If this is an important difference, the NULL case could perform a global lookup, + * only returning `true` if the sequence strip exists elsewhere + * (ignoring it's selection state). */ + if (seq == NULL) { + return true; + } + + if ((seq->flag & SELECT) == 0) { return true; } } -- cgit v1.2.3 From 684500837d2994a2da840456cbff8c04b4371e00 Mon Sep 17 00:00:00 2001 From: Rajesh Advani Date: Fri, 3 Sep 2021 10:45:48 +0200 Subject: Cleanup: convert function nodes socket list to use new API The new API was introduced in rB1e69a25043120cc8dddc3f58622eb50e1443def1. Differential Revision: https://developer.blender.org/D12380 --- source/blender/nodes/function/node_function_util.hh | 1 + .../nodes/function/nodes/node_fn_boolean_math.cc | 18 +++++++++--------- .../nodes/function/nodes/node_fn_float_compare.cc | 20 ++++++++++---------- .../nodes/function/nodes/node_fn_float_to_int.cc | 16 ++++++++-------- .../nodes/function/nodes/node_fn_input_string.cc | 12 ++++++++---- .../nodes/function/nodes/node_fn_input_vector.cc | 12 ++++++++---- .../nodes/function/nodes/node_fn_random_float.cc | 20 ++++++++++---------- 7 files changed, 54 insertions(+), 45 deletions(-) diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh index 96a8f29c3e9..46b485298e3 100644 --- a/source/blender/nodes/function/node_function_util.hh +++ b/source/blender/nodes/function/node_function_util.hh @@ -31,6 +31,7 @@ #include "NOD_function.h" #include "NOD_multi_function.hh" +#include "NOD_socket_declarations.hh" #include "node_util.h" diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 58e7d82beea..b71ee092de6 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -24,17 +24,17 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_boolean_math_in[] = { - {SOCK_BOOLEAN, N_("Boolean")}, - {SOCK_BOOLEAN, N_("Boolean")}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_boolean_math_out[] = { - {SOCK_BOOLEAN, N_("Boolean")}, - {-1, ""}, +static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Boolean", "Boolean"); + b.add_input("Boolean", "Boolean_001"); + b.add_output("Boolean"); }; +} // namespace blender::nodes + static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -91,7 +91,7 @@ void register_node_type_fn_boolean_math() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); + ntype.declare = blender::nodes::fn_node_boolean_math_declare; node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); ntype.build_multi_function = fn_node_boolean_math_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 918dd24e520..4f4830afabc 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -26,18 +26,18 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_float_compare_in[] = { - {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Epsilon"), 0.001f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_float_compare_out[] = { - {SOCK_BOOLEAN, N_("Result")}, - {-1, ""}, +static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) +{ + b.add_input("A").min(-10000.0f).max(10000.0f); + b.add_input("B").min(-10000.0f).max(10000.0f); + b.add_input("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); + b.add_output("Result"); }; +} // namespace blender::nodes + static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -110,7 +110,7 @@ void register_node_type_fn_float_compare() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); + ntype.declare = blender::nodes::fn_node_float_compare_declare; node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); ntype.build_multi_function = fn_node_float_compare_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index 40b8f27f895..e59c78d2c04 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -25,16 +25,16 @@ #include "node_function_util.hh" -static bNodeSocketTemplate fn_node_float_to_int_in[] = { - {SOCK_FLOAT, N_("Float"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_float_to_int_out[] = { - {SOCK_INT, N_("Integer")}, - {-1, ""}, +static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Float"); + b.add_output("Integer"); }; +} // namespace blender::nodes + static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); @@ -88,7 +88,7 @@ void register_node_type_fn_float_to_int() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out); + ntype.declare = blender::nodes::fn_node_float_to_int_declare; node_type_label(&ntype, node_float_to_int_label); ntype.build_multi_function = fn_node_float_to_int_build_multi_function; ntype.draw_buttons = fn_node_float_to_int_layout; diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 560ace57aba..4a8e898fb9b 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -19,11 +19,15 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate fn_node_input_string_out[] = { - {SOCK_STRING, N_("String")}, - {-1, ""}, +namespace blender::nodes { + +static void fn_node_input_string_declare(NodeDeclarationBuilder &b) +{ + b.add_output("String"); }; +} // namespace blender::nodes + static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "string", 0, "", ICON_NONE); @@ -75,7 +79,7 @@ void register_node_type_fn_input_string() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out); + ntype.declare = blender::nodes::fn_node_input_string_declare; node_type_init(&ntype, fn_node_input_string_init); node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); ntype.build_multi_function = fn_node_input_string_build_multi_function; diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 244c045de9a..9548df7b423 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -21,11 +21,15 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate fn_node_input_vector_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +namespace blender::nodes { + +static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Vector"); }; +} // namespace blender::nodes + static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col = uiLayoutColumn(layout, true); @@ -52,7 +56,7 @@ void register_node_type_fn_input_vector() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0, 0); - node_type_socket_templates(&ntype, nullptr, fn_node_input_vector_out); + ntype.declare = blender::nodes::fn_node_input_vector_declare; node_type_init(&ntype, fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc index 47ec9adf6bd..1bd39aacdca 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc @@ -18,18 +18,18 @@ #include "BLI_hash.h" -static bNodeSocketTemplate fn_node_random_float_in[] = { - {SOCK_FLOAT, N_("Min"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Max"), 1.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_INT, N_("Seed"), 0, 0, 0, 0, -10000, 10000}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate fn_node_random_float_out[] = { - {SOCK_FLOAT, N_("Value")}, - {-1, ""}, +static void fn_node_random_float_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Min").min(-10000.0f).max(10000.0f); + b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input("Seed").min(-10000).max(10000); + b.add_output("Value"); }; +} // namespace blender::nodes + class RandomFloatFunction : public blender::fn::MultiFunction { public: RandomFloatFunction() @@ -79,7 +79,7 @@ void register_node_type_fn_random_float() static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); - node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out); + ntype.declare = blender::nodes::fn_node_random_float_declare; ntype.build_multi_function = fn_node_random_float_build_multi_function; nodeRegisterType(&ntype); } -- cgit v1.2.3 From 671640b4c79b05677333975fa3714e31fb75afdd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Sep 2021 19:41:03 +1000 Subject: Cleanup: use bool for RNA path token extraction --- source/blender/makesrna/intern/rna_access.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 51e20eb9e7f..f9274936226 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4859,7 +4859,7 @@ PointerRNA rna_array_lookup_int( /* RNA Path - Experiment */ -static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int bracket) +static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, const bool bracket) { const char *p; int len = 0; @@ -4939,17 +4939,17 @@ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int return buf; } -static int rna_token_strip_quotes(char *token) +static bool rna_token_strip_quotes(char *token) { if (token[0] == '"') { int len = strlen(token); if (len >= 2 && token[len - 1] == '"') { /* strip away "" */ token[len - 1] = '\0'; - return 1; + return true; } } - return 0; + return false; } static bool rna_path_parse_collection_key(const char **path, @@ -4971,7 +4971,7 @@ static bool rna_path_parse_collection_key(const char **path, char *token; /* resolve the lookup with [] brackets */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1); + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), true); if (!token) { return false; @@ -5041,7 +5041,7 @@ static bool rna_path_parse_array_index(const char **path, /* multi index resolve */ if (**path == '[') { - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 1); + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), true); if (token == NULL) { /* invalid syntax blah[] */ @@ -5066,7 +5066,7 @@ static bool rna_path_parse_array_index(const char **path, } else if (dim == 1) { /* location.x || scale.X, single dimension arrays only */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), 0); + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), false); if (token == NULL) { /* invalid syntax blah. */ return false; @@ -5166,7 +5166,7 @@ static bool rna_path_parse(PointerRNA *ptr, RNA_POINTER_INVALIDATE(&nextptr); } - int use_id_prop = (*path == '[') ? 1 : 0; + const bool use_id_prop = (*path == '['); char *token; /* custom property lookup ? * C.object["someprop"] @@ -5492,7 +5492,7 @@ char *RNA_path_back(const char *path) while (*current) { char *token; - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 0); + token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), false); if (!token) { return NULL; @@ -5502,7 +5502,7 @@ char *RNA_path_back(const char *path) } /* in case of collection we also need to strip off [] */ - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), 1); + token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), true); if (token && token != fixedbuf) { MEM_freeN(token); } -- cgit v1.2.3 From d97fd305a005170ea2c15f3697238e2680db679e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Sep 2021 19:41:05 +1000 Subject: RNA: minor optimize for token extraction of RNA paths - Split rna_path_token in two, extracting bracket handling into it's own function. - Only handle escape characters for quoted tokens. Numbers were copied using BLI_str_unescape which is unnecessary. - Extract text without without quotes, use a return argument so the caller can tell if the token was quoted. This avoids having to strip the tokens quotes afterwards. --- source/blender/makesrna/intern/rna_access.c | 173 +++++++++++++++++----------- 1 file changed, 105 insertions(+), 68 deletions(-) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index f9274936226..75df8f929e8 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -4859,75 +4859,119 @@ PointerRNA rna_array_lookup_int( /* RNA Path - Experiment */ -static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, const bool bracket) +/** + * Extract the first token from `path`. + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * \return The nil terminated token. + */ +static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen) { - const char *p; int len = 0; - if (bracket) { - /* get data between [], check escaping quotes and back-slashes with #BLI_str_unescape. */ - if (**path == '[') { - (*path)++; - } - else { - return NULL; - } + /* Get data until `.` or `[`. */ + const char *p = *path; + while (*p && !ELEM(*p, '.', '[')) { + len++; + p++; + } - p = *path; + /* Empty, return. */ + if (UNLIKELY(len == 0)) { + return NULL; + } - /* 2 kinds of look-ups now, quoted or unquoted. */ - if (*p != '"') { - while (*p && (*p != ']')) { - len++; - p++; - } - } - else { - const char *p_end = BLI_str_escape_find_quote(p + 1); - if (p_end == NULL) { - /* No Matching quote. */ - return NULL; - } - /* Skip the last quoted char to get the `]`. */ - p_end += 1; + /* Try to use fixed buffer if possible. */ + char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); + memcpy(buf, *path, sizeof(char) * len); + buf[len] = '\0'; - len += (p_end - p); - p = p_end; - } + if (*p == '.') { + p++; + } + *path = p; - if (*p != ']') { + return buf; +} + +/** + * Extract the first token in brackets from `path` (with quoted text support). + * + * - `[0]` -> `0` + * - `["Some\"Quote"]` -> `Some"Quote` + * + * \param path: Extract the token from path, step the pointer to the beginning of the next token + * (past quoted text and brackets). + * \return The nil terminated token. + */ +static char *rna_path_token_in_brackets(const char **path, + char *fixedbuf, + int fixedlen, + bool *r_quoted) +{ + int len = 0; + bool quoted = false; + + BLI_assert(r_quoted != NULL); + + /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */ + if (UNLIKELY(**path != '[')) { + return NULL; + } + + (*path)++; + const char *p = *path; + + /* 2 kinds of look-ups now, quoted or unquoted. */ + if (*p == '"') { + /* Find the matching quote. */ + (*path)++; + p = *path; + const char *p_end = BLI_str_escape_find_quote(p); + if (p_end == NULL) { + /* No Matching quote. */ return NULL; } + /* Exclude the last quote from the length. */ + len += (p_end - p); + + /* Skip the last quoted char to get the `]`. */ + p_end += 1; + p = p_end; + quoted = true; } else { - /* Get data until `.` or `[`. */ - p = *path; - - while (*p && *p != '.' && *p != '[') { + /* Find the matching bracket. */ + while (*p && (*p != ']')) { len++; p++; } } - /* empty, return */ - if (len == 0) { + if (UNLIKELY(*p != ']')) { + return NULL; + } + + /* Empty, return. */ + if (UNLIKELY(len == 0)) { return NULL; } /* Try to use fixed buffer if possible. */ char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__); - /* copy string, taking into account escaped ] */ - if (bracket) { + /* Copy string, taking into account escaped ']' */ + if (quoted) { BLI_str_unescape(buf, *path, len); - p = (*path) + len; + /* +1 to step over the last quote. */ + BLI_assert((*path)[len] == '"'); + p = (*path) + len + 1; } else { memcpy(buf, *path, sizeof(char) * len); buf[len] = '\0'; } - - /* set path to start of next token */ + /* Set path to start of next token. */ if (*p == ']') { p++; } @@ -4936,20 +4980,9 @@ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, con } *path = p; - return buf; -} + *r_quoted = quoted; -static bool rna_token_strip_quotes(char *token) -{ - if (token[0] == '"') { - int len = strlen(token); - if (len >= 2 && token[len - 1] == '"') { - /* strip away "" */ - token[len - 1] = '\0'; - return true; - } - } - return false; + return buf; } static bool rna_path_parse_collection_key(const char **path, @@ -4968,18 +5001,19 @@ static bool rna_path_parse_collection_key(const char **path, } if (**path == '[') { + bool quoted; char *token; /* resolve the lookup with [] brackets */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), true); + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); if (!token) { return false; } /* check for "" to see if it is a string */ - if (rna_token_strip_quotes(token)) { - if (RNA_property_collection_lookup_string(ptr, prop, token + 1, r_nextptr)) { + if (quoted) { + if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) { /* pass */ } else { @@ -5041,15 +5075,16 @@ static bool rna_path_parse_array_index(const char **path, /* multi index resolve */ if (**path == '[') { - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), true); + bool quoted; + token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), "ed); if (token == NULL) { /* invalid syntax blah[] */ return false; } /* check for "" to see if it is a string */ - if (rna_token_strip_quotes(token)) { - temp_index = RNA_property_array_item_index(prop, *(token + 1)); + if (quoted) { + temp_index = RNA_property_array_item_index(prop, *token); } else { /* otherwise do int lookup */ @@ -5066,7 +5101,7 @@ static bool rna_path_parse_array_index(const char **path, } else if (dim == 1) { /* location.x || scale.X, single dimension arrays only */ - token = rna_path_token(path, fixedbuf, sizeof(fixedbuf), false); + token = rna_path_token(path, fixedbuf, sizeof(fixedbuf)); if (token == NULL) { /* invalid syntax blah. */ return false; @@ -5167,7 +5202,6 @@ static bool rna_path_parse(PointerRNA *ptr, } const bool use_id_prop = (*path == '['); - char *token; /* custom property lookup ? * C.object["someprop"] */ @@ -5177,8 +5211,10 @@ static bool rna_path_parse(PointerRNA *ptr, } /* look up property name in current struct */ - token = rna_path_token(&path, fixedbuf, sizeof(fixedbuf), use_id_prop); - + bool quoted = false; + char *token = use_id_prop ? + rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), "ed) : + rna_path_token(&path, fixedbuf, sizeof(fixedbuf)); if (!token) { return false; } @@ -5186,8 +5222,8 @@ static bool rna_path_parse(PointerRNA *ptr, prop = NULL; if (use_id_prop) { /* look up property name in current struct */ IDProperty *group = RNA_struct_idprops(&curptr, 0); - if (group && rna_token_strip_quotes(token)) { - prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token + 1); + if (group && quoted) { + prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token); } } else { @@ -5492,7 +5528,7 @@ char *RNA_path_back(const char *path) while (*current) { char *token; - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), false); + token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf)); if (!token) { return NULL; @@ -5502,7 +5538,8 @@ char *RNA_path_back(const char *path) } /* in case of collection we also need to strip off [] */ - token = rna_path_token(¤t, fixedbuf, sizeof(fixedbuf), true); + bool quoted; + token = rna_path_token_in_brackets(¤t, fixedbuf, sizeof(fixedbuf), "ed); if (token && token != fixedbuf) { MEM_freeN(token); } -- cgit v1.2.3 From f530b435501b36ea5621eacbd2624db11864b8c3 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 3 Sep 2021 12:42:11 +0200 Subject: Fix T91159: GPencil Smooth brush is using Affect Pressure but not used The parameter wa sin the UI but was not used because it was replaced by Use Thickness. --- release/scripts/startup/bl_ui/properties_grease_pencil_common.py | 3 --- source/blender/blenkernel/intern/brush.c | 3 +-- source/blender/makesdna/DNA_brush_enums.h | 2 -- source/blender/makesrna/intern/rna_brush.c | 7 ------- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index f87f5351d6d..f01e75dbab8 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -85,9 +85,6 @@ class GreasePencilSculptOptionsPanel: layout.prop(gp_settings, "use_edit_strength", text="Affect Strength") layout.prop(gp_settings, "use_edit_thickness", text="Affect Thickness") - if tool == 'SMOOTH': - layout.prop(gp_settings, "use_edit_pressure") - layout.prop(gp_settings, "use_edit_uv", text="Affect UV") diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 7b81187be21..d60ef28efda 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1127,7 +1127,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_strength = 0.3f; brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; + brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAGMODE_APPLY_THICKNESS; brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; break; @@ -1141,7 +1141,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_strength = 0.3f; brush->gpencil_settings->flag |= GP_BRUSH_USE_STRENGTH_PRESSURE; - brush->gpencil_settings->sculpt_flag = GP_SCULPT_FLAG_SMOOTH_PRESSURE; brush->gpencil_settings->sculpt_mode_flag |= GP_SCULPT_FLAGMODE_APPLY_POSITION; break; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 6563afd2b4a..72b2db89cef 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -305,8 +305,6 @@ typedef enum eGp_Vertex_Mode { typedef enum eGP_Sculpt_Flag { /* invert the effect of the brush */ GP_SCULPT_FLAG_INVERT = (1 << 0), - /* smooth brush affects pressure values as well */ - GP_SCULPT_FLAG_SMOOTH_PRESSURE = (1 << 2), /* temporary invert action */ GP_SCULPT_FLAG_TMP_INVERT = (1 << 3), } eGP_Sculpt_Flag; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index cdca58df4b0..25caa411979 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1801,13 +1801,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - prop = RNA_def_property(srna, "use_edit_pressure", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "sculpt_flag", GP_SCULPT_FLAG_SMOOTH_PRESSURE); - RNA_def_property_ui_text( - prop, "Affect Pressure", "Affect pressure values as well when smoothing strokes"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "sculpt_flag"); RNA_def_property_enum_items(prop, prop_direction_items); -- cgit v1.2.3 From 0950cfd9d53ef666cf820765e83586f72b04e9b6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Sep 2021 21:18:03 +1000 Subject: PyAPI: add read-only 'is_valid' attribute to mathutils types There was no convenient way to check if the owner of a mathutils type was valid. Added to support issue reported in T91111. --- source/blender/python/mathutils/mathutils.c | 16 ++++++++++++++++ source/blender/python/mathutils/mathutils.h | 5 +++++ source/blender/python/mathutils/mathutils_Color.c | 5 +++++ source/blender/python/mathutils/mathutils_Euler.c | 5 +++++ source/blender/python/mathutils/mathutils_Matrix.c | 5 +++++ source/blender/python/mathutils/mathutils_Quaternion.c | 5 +++++ source/blender/python/mathutils/mathutils_Vector.c | 5 +++++ 7 files changed, 46 insertions(+) diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index be7dae6871b..0043fc36162 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -599,6 +599,15 @@ uchar Mathutils_RegisterCallback(Mathutils_Callback *cb) return i; } +int _BaseMathObject_CheckCallback(BaseMathObject *self) +{ + Mathutils_Callback *cb = mathutils_callbacks[self->cb_type]; + if (LIKELY(cb->check(self) != -1)) { + return 0; + } + return -1; +} + /* use macros to check for NULL */ int _BaseMathObject_ReadCallback(BaseMathObject *self) { @@ -687,6 +696,13 @@ PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *UNUSED(closur return PyBool_FromLong((self->flag & BASE_MATH_FLAG_IS_FROZEN) != 0); } +char BaseMathObject_is_valid_doc[] = + "True when the owner of this data is valid.\n\n:type: boolean"; +PyObject *BaseMathObject_is_valid_get(BaseMathObject *self, void *UNUSED(closure)) +{ + return PyBool_FromLong(BaseMath_CheckCallback(self) == 0); +} + char BaseMathObject_freeze_doc[] = ".. function:: freeze()\n" "\n" diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index 80be841785a..4aa26dcc5be 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -28,6 +28,7 @@ struct DynStr; extern char BaseMathObject_is_wrapped_doc[]; extern char BaseMathObject_is_frozen_doc[]; +extern char BaseMathObject_is_valid_doc[]; extern char BaseMathObject_owner_doc[]; #define BASE_MATH_NEW(struct_name, root_type, base_type) \ @@ -81,6 +82,7 @@ typedef struct { PyObject *BaseMathObject_owner_get(BaseMathObject *self, void *); PyObject *BaseMathObject_is_wrapped_get(BaseMathObject *self, void *); PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *); +PyObject *BaseMathObject_is_valid_get(BaseMathObject *self, void *); extern char BaseMathObject_freeze_doc[]; PyObject *BaseMathObject_freeze(BaseMathObject *self); @@ -117,6 +119,7 @@ struct Mathutils_Callback { unsigned char Mathutils_RegisterCallback(Mathutils_Callback *cb); +int _BaseMathObject_CheckCallback(BaseMathObject *self); int _BaseMathObject_ReadCallback(BaseMathObject *self); int _BaseMathObject_WriteCallback(BaseMathObject *self); int _BaseMathObject_ReadIndexCallback(BaseMathObject *self, int index); @@ -126,6 +129,8 @@ void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self); void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self); /* since this is called so often avoid where possible */ +#define BaseMath_CheckCallback(_self) \ + (((_self)->cb_user ? _BaseMathObject_CheckCallback((BaseMathObject *)_self) : 0)) #define BaseMath_ReadCallback(_self) \ (((_self)->cb_user ? _BaseMathObject_ReadCallback((BaseMathObject *)_self) : 0)) #define BaseMath_WriteCallback(_self) \ diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 7546f2ef730..13d712bddb0 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -866,6 +866,11 @@ static PyGetSetDef Color_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index 595d03b533b..1033d186fca 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -699,6 +699,11 @@ static PyGetSetDef Euler_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 36b8b0b6d35..ce04a143aae 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -3148,6 +3148,11 @@ static PyGetSetDef Matrix_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 77a30dcd447..525b2da7d06 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -1505,6 +1505,11 @@ static PyGetSetDef Quaternion_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index efcaa9b6a51..23758c5603e 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -2551,6 +2551,11 @@ static PyGetSetDef Vector_getseters[] = { (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, + {"is_valid", + (getter)BaseMathObject_is_valid_get, + (setter)NULL, + BaseMathObject_is_valid_doc, + NULL}, {"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, /* Auto-generated swizzle attributes, see Python script above. */ -- cgit v1.2.3 From f9ccd26b037d43f2490d1f0263e45e775d30473d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 3 Sep 2021 21:58:52 +1000 Subject: Fix T87768: `.path_resolve` fails when requested property is None. Add a version of RNA_path_resolve_full that returns true when the path resolves to a NULL RNA pointer. --- source/blender/makesrna/RNA_access.h | 2 ++ source/blender/makesrna/intern/rna_access.c | 12 ++++++++++++ source/blender/python/intern/bpy_rna.c | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 76155973982..75057c1a071 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1124,6 +1124,8 @@ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, Prop bool RNA_path_resolve_full( PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); +bool RNA_path_resolve_full_maybe_null( + PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index); /* path_resolve_property() variants ensure that pointer + property both exist */ bool RNA_path_resolve_property(PointerRNA *ptr, diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 75df8f929e8..0ba5b786187 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5361,6 +5361,18 @@ bool RNA_path_resolve_full( return r_ptr->data != NULL; } +/** + * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data. + * + * \note While it's correct to ignore the value of #PointerRNA.data + * most callers need to know if the resulting pointer was found and not null. + */ +bool RNA_path_resolve_full_maybe_null( + PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index) +{ + return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true); +} + /** * Resolve the given RNA Path to find both the pointer AND property * indicated by fully resolving the path. diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 9d0755a865d..35acb56e66a 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -3663,7 +3663,7 @@ static PyObject *pyrna_struct_path_resolve(BPy_StructRNA *self, PyObject *args) return NULL; } - if (RNA_path_resolve_full(&self->ptr, path, &r_ptr, &r_prop, &index)) { + if (RNA_path_resolve_full_maybe_null(&self->ptr, path, &r_ptr, &r_prop, &index)) { if (r_prop) { if (index != -1) { if (index >= RNA_property_array_length(&r_ptr, r_prop) || index < 0) { -- cgit v1.2.3 From ae334532cffb2dd9074454b9a7ba095430f18735 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 3 Sep 2021 15:24:01 +0200 Subject: GPencil: Smooth thickness when joining strokes When joining two strokes in paint mode using the auto merge option, the join was very hard if the thickness was too different. This patch adds a smooth to the join in order to get better transition. Also fixed the problem to join existing strokes very far from actual stroke. Some cleanup and rename of old code is included in order to make code more readable. Reviewed By: pepeland Differential Revision: https://developer.blender.org/D12362 --- source/blender/blenkernel/BKE_gpencil_geom.h | 5 +-- source/blender/blenkernel/intern/gpencil_geom.cc | 40 ++++++++++++++++++++-- source/blender/editors/gpencil/gpencil_edit.c | 4 +-- source/blender/editors/gpencil/gpencil_fill.c | 2 +- .../blender/editors/gpencil/gpencil_interpolate.c | 2 +- source/blender/editors/gpencil/gpencil_paint.c | 21 +++++++++--- source/blender/editors/gpencil/gpencil_primitive.c | 16 +++++++-- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 2 +- source/blender/editors/gpencil/gpencil_utils.c | 27 ++++++++++++++- source/blender/editors/include/ED_gpencil.h | 7 ++++ .../gpencil_modifiers/intern/MOD_gpencilsmooth.c | 2 +- 11 files changed, 111 insertions(+), 17 deletions(-) diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index 29e3a74b1b2..d472fd6f02b 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -101,7 +101,7 @@ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, const float dist, const bool select); -bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf); +bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int i, float inf); bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence); bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence); bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence); @@ -151,7 +151,8 @@ void BKE_gpencil_stroke_set_random_color(struct bGPDstroke *gps); void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a, struct bGPDstroke *gps_b, const bool leave_gaps, - const bool fit_thickness); + const bool fit_thickness, + const bool smooth); void BKE_gpencil_stroke_copy_to_keyframes(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDframe *gpf, diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 5bca20ecd44..8ff026231f5 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -800,7 +800,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo * \param i: Point index * \param inf: Amount of smoothing to apply */ -bool BKE_gpencil_stroke_smooth(bGPDstroke *gps, int i, float inf) +bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps, int i, float inf) { bGPDspoint *pt = &gps->points[i]; float sco[3] = {0.0f}; @@ -3248,7 +3248,8 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, void BKE_gpencil_stroke_join(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps, - const bool fit_thickness) + const bool fit_thickness, + const bool smooth) { bGPDspoint point; bGPDspoint *pt; @@ -3326,16 +3327,51 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime); } + /* Ratio to apply in the points to keep the same thickness in the joined stroke using the + * destination stroke thickness. */ const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ? (float)gps_b->thickness / (float)gps_a->thickness : 1.0f; /* 3rd: add all points */ + const int totpoints_a = gps_a->totpoints; for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr; gpencil_stroke_copy_point( gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime); } + /* Smooth the join to avoid hard thickness changes. */ + if (smooth) { + const int sample_points = 8; + /* Get the segment to smooth using n points on each side of the join. */ + int start = MAX2(0, totpoints_a - sample_points); + int end = MIN2(gps_a->totpoints - 1, start + (sample_points * 2)); + const int len = (end - start); + float step = 1.0f / ((len / 2) + 1); + + /* Calc the average pressure. */ + float avg_pressure = 0.0f; + for (i = start; i < end; i++) { + pt = &gps_a->points[i]; + avg_pressure += pt->pressure; + } + avg_pressure = avg_pressure / len; + + /* Smooth segment thickness and position. */ + float ratio = step; + for (i = start; i < end; i++) { + pt = &gps_a->points[i]; + pt->pressure += (avg_pressure - pt->pressure) * ratio; + BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f); + + ratio += step; + /* In the center, reverse the ratio. */ + if (ratio > 1.0f) { + ratio = ratio - step - step; + step *= -1.0f; + } + } + } } /* Copy the stroke of the frame to all frames selected (except current). */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 8d1f841da6c..aa3178ddc2c 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3618,7 +3618,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) } elem = &strokes_list[i]; /* Join new_stroke and stroke B. */ - BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true); + BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true, false); elem->used = true; } @@ -3967,7 +3967,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op) /* perform smoothing */ if (smooth_position) { - BKE_gpencil_stroke_smooth(gps, i, factor); + BKE_gpencil_stroke_smooth_point(gps, i, factor); } if (smooth_strength) { BKE_gpencil_stroke_smooth_strength(gps, i, factor); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 0c88d678ef4..f5474a7cdc3 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1569,7 +1569,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) float smoothfac = 1.0f; for (int r = 0; r < 1; r++) { for (int i = 0; i < gps->totpoints; i++) { - BKE_gpencil_stroke_smooth(gps, i, smoothfac - reduce); + BKE_gpencil_stroke_smooth_point(gps, i, smoothfac - reduce); } reduce += 0.25f; /* reduce the factor */ } diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index a8bd3b11bb1..fdd9f44605e 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -333,7 +333,7 @@ static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps, float reduce = 0.0f; for (int r = 0; r < smooth_steps; r++) { for (int i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, smooth_factor - reduce); + BKE_gpencil_stroke_smooth_point(gps, i, smooth_factor - reduce); BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor); } reduce += 0.25f; /* reduce the factor */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 28a22633742..9b157224178 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1209,7 +1209,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) float reduce = 0.0f; for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) { for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_stroke_smooth_point( + gps, i, brush->gpencil_settings->draw_smoothfac - reduce); BKE_gpencil_stroke_smooth_strength(gps, i, brush->gpencil_settings->draw_smoothfac); } reduce += 0.25f; /* reduce the factor */ @@ -1221,7 +1222,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) float ifac = (float)brush->gpencil_settings->input_samples / 10.0f; float sfac = interpf(1.0f, 0.2f, ifac); for (i = 0; i < gps->totpoints - 1; i++) { - BKE_gpencil_stroke_smooth(gps, i, sfac); + BKE_gpencil_stroke_smooth_point(gps, i, sfac); BKE_gpencil_stroke_smooth_strength(gps, i, sfac); } } @@ -1288,11 +1289,23 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) /* Join with existing strokes. */ if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) { if (gps->prev != NULL) { + BKE_gpencil_stroke_boundingbox_calc(gps); + float diff_mat[4][4], ctrl1[2], ctrl2[2]; + BKE_gpencil_layer_transform_matrix_get(depsgraph, p->ob, gpl, diff_mat); + ED_gpencil_stroke_extremes_to2d(&p->gsc, diff_mat, gps, ctrl1, ctrl2); + int pt_index = 0; bool doit = true; while (doit && gps) { - bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends( - p->C, &p->gsc, gpl, gpl->actframe, gps, GPENCIL_MINIMUM_JOIN_DIST, &pt_index); + bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends(p->C, + &p->gsc, + gpl, + gpl->actframe, + gps, + ctrl1, + ctrl2, + GPENCIL_MINIMUM_JOIN_DIST, + &pt_index); if (gps_target != NULL) { gps = ED_gpencil_stroke_join_and_trim(p->gpd, p->gpf, gps, gps_target, pt_index); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 5ecb6d9a212..f8cfc130e35 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1382,11 +1382,23 @@ static void gpencil_primitive_interaction_end(bContext *C, if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) { if (ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_CURVE, GP_STROKE_POLYLINE)) { if (gps->prev != NULL) { + BKE_gpencil_stroke_boundingbox_calc(gps); + float diff_mat[4][4], ctrl1[2], ctrl2[2]; + BKE_gpencil_layer_transform_matrix_get(tgpi->depsgraph, tgpi->ob, tgpi->gpl, diff_mat); + ED_gpencil_stroke_extremes_to2d(&tgpi->gsc, diff_mat, gps, ctrl1, ctrl2); + int pt_index = 0; bool doit = true; while (doit && gps) { - bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends( - C, &tgpi->gsc, tgpi->gpl, gpf, gps, GPENCIL_MINIMUM_JOIN_DIST, &pt_index); + bGPDstroke *gps_target = ED_gpencil_stroke_nearest_to_ends(C, + &tgpi->gsc, + tgpi->gpl, + gpf, + gps, + ctrl1, + ctrl2, + GPENCIL_MINIMUM_JOIN_DIST, + &pt_index); if (gps_target != NULL) { gps = ED_gpencil_stroke_join_and_trim(tgpi->gpd, gpf, gps, gps_target, pt_index); } diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 869254cef3b..e9a6beab798 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -337,7 +337,7 @@ static bool gpencil_brush_smooth_apply(tGP_BrushEditData *gso, /* perform smoothing */ if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) { - BKE_gpencil_stroke_smooth(gps, pt_index, inf); + BKE_gpencil_stroke_smooth_point(gps, pt_index, inf); } if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) { BKE_gpencil_stroke_smooth_strength(gps, pt_index, inf); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 5cc52303cd6..72d10d840fa 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -3213,11 +3213,28 @@ bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, return hit; } +/* Get extremes of stroke in 2D using current view. */ +void ED_gpencil_stroke_extremes_to2d(const GP_SpaceConversion *gsc, + const float diff_mat[4][4], + bGPDstroke *gps, + float r_ctrl1[2], + float r_ctrl2[2]) +{ + bGPDspoint pt_dummy_ps; + + gpencil_point_to_parent_space(&gps->points[0], diff_mat, &pt_dummy_ps); + gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl1[0], &r_ctrl1[1]); + gpencil_point_to_parent_space(&gps->points[gps->totpoints - 1], diff_mat, &pt_dummy_ps); + gpencil_point_to_xy_fl(gsc, gps, &pt_dummy_ps, &r_ctrl2[0], &r_ctrl2[1]); +} + bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, const GP_SpaceConversion *gsc, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, + const float ctrl1[2], + const float ctrl2[2], const float radius, int *r_index) { @@ -3267,6 +3284,14 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_target_end[0], &pt2d_target_end[1]); + /* If the distance to the original stroke extremes is too big, the stroke must not be joined. */ + if ((len_squared_v2v2(ctrl1, pt2d_target_start) > radius_sqr) && + (len_squared_v2v2(ctrl1, pt2d_target_end) > radius_sqr) && + (len_squared_v2v2(ctrl2, pt2d_target_start) > radius_sqr) && + (len_squared_v2v2(ctrl2, pt2d_target_end) > radius_sqr)) { + continue; + } + if ((len_squared_v2v2(pt2d_start, pt2d_target_start) > radius_sqr) && (len_squared_v2v2(pt2d_start, pt2d_target_end) > radius_sqr) && (len_squared_v2v2(pt2d_end, pt2d_target_start) > radius_sqr) && @@ -3350,7 +3375,7 @@ bGPDstroke *ED_gpencil_stroke_join_and_trim( /* Join both strokes. */ int totpoint = gps_final->totpoints; - BKE_gpencil_stroke_join(gps_final, gps, false, true); + BKE_gpencil_stroke_join(gps_final, gps, false, true, true); /* Select the join points and merge if the distance is very small. */ pt = &gps_final->points[totpoint - 1]; diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 8a8d91a570c..c760b661373 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -391,8 +391,15 @@ struct bGPDstroke *ED_gpencil_stroke_nearest_to_ends(struct bContext *C, struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, + const float ctrl1[2], + const float ctrl2[2], const float radius, int *r_index); +void ED_gpencil_stroke_extremes_to2d(const struct GP_SpaceConversion *gsc, + const float diff_mat[4][4], + struct bGPDstroke *gps, + float r_ctrl1[2], + float r_ctrl2[2]); struct bGPDstroke *ED_gpencil_stroke_join_and_trim(struct bGPdata *gpd, struct bGPDframe *gpf, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c index 4142a1c02dc..b00db1ba2d2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c @@ -132,7 +132,7 @@ static void deformStroke(GpencilModifierData *md, const float val = mmd->factor * weight; /* perform smoothing */ if (mmd->flag & GP_SMOOTH_MOD_LOCATION) { - BKE_gpencil_stroke_smooth(gps, i, val); + BKE_gpencil_stroke_smooth_point(gps, i, val); } if (mmd->flag & GP_SMOOTH_MOD_STRENGTH) { BKE_gpencil_stroke_smooth_strength(gps, i, val); -- cgit v1.2.3 From ac97893dfc77fd77e0254a0a4692820c7dbc6967 Mon Sep 17 00:00:00 2001 From: Leon Leno Date: Fri, 3 Sep 2021 16:13:29 +0200 Subject: Fix T88411: Draw frame node text when label is empty This patch fixes the issue described in T88411, that the text in frame nodes is only shown, when the node has a label. This has been caused by rB8f04ddbbc626, because `node_draw_frame_label` not only draws the label, but also all the other text. Therefore skipping it, when the label is empty, also skips drawing the other text. This is fixed by moving the check for the empty label into `node_frame_draw_label`. **Patch:** Frame nodes show text despite not having a label. {F10286204, size = full} **Same setup in master:** {F10128099, size = full} **Test file** {F10128102} Reviewed By: #user_interface, pablovazquez Maniphest Tasks: T88411 Differential Revision: https://developer.blender.org/D11315 --- source/blender/editors/space_node/drawnode.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 0f7a911e3ce..4b859de0ac9 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -362,8 +362,12 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp float x = BLI_rctf_cent_x(rct) - (0.5f * width); float y = rct->ymax - label_height; - BLF_position(fontid, x, y, 0); - BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + /* label */ + const bool has_label = node->label[0] != '\0'; + if (has_label) { + BLF_position(fontid, x, y, 0); + BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + } /* draw text body */ if (node->id) { @@ -374,7 +378,8 @@ static void node_draw_frame_label(bNodeTree *ntree, bNode *node, const float asp /* 'x' doesn't need aspect correction */ x = rct->xmin + margin; - y = rct->ymax - (label_height + line_spacing); + y = rct->ymax - label_height - (has_label ? line_spacing : 0); + /* early exit */ int y_min = y + ((margin * 2) - (y - rct->ymin)); @@ -455,10 +460,8 @@ static void node_draw_frame(const bContext *C, UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); } - /* label */ - if (node->label[0] != '\0') { - node_draw_frame_label(ntree, node, snode->runtime->aspect); - } + /* label and text */ + node_draw_frame_label(ntree, node, snode->runtime->aspect); UI_block_end(C, node->block); UI_block_draw(C, node->block); -- cgit v1.2.3 From a45dd52cf0bbdc0ff362681a4836385c8154c70d Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 3 Sep 2021 12:17:00 +0200 Subject: Depsgraph: skip parentbone relation if bone prop is empty Clearing the Parent Bone field in relations would result in something like this: ``` add_relation(Bone Parent) - Could not find op_from (ComponentKey(OBArmature, BONE)) add_relation(Bone Parent) - Failed, but op_to (ComponentKey(OBEmpty, TRANSFORM)) was ok ERROR (bke.object): /source/blender/blenkernel/intern\object.c:3330 ob_parbone: Object Empty with Bone parent: bone doesn't exist ``` Now skip creation of a depsgraph relation if the Parent Bone field is empty (since this would be invalid anyways). ref. T91101 Maniphest Tasks: T91101 Differential Revision: https://developer.blender.org/D12389 --- .../depsgraph/intern/builder/deg_builder_relations.cc | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index ef1db8be933..28cfc5a9e1a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1006,11 +1006,13 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object) /* Bone Parent */ case PARBONE: { - ComponentKey parent_bone_key(parent_id, NodeType::BONE, object->parsubstr); - OperationKey parent_transform_key( - parent_id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); - add_relation(parent_bone_key, object_transform_key, "Bone Parent"); - add_relation(parent_transform_key, object_transform_key, "Armature Parent"); + if (object->parsubstr[0] != '\0') { + ComponentKey parent_bone_key(parent_id, NodeType::BONE, object->parsubstr); + OperationKey parent_transform_key( + parent_id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); + add_relation(parent_bone_key, object_transform_key, "Bone Parent"); + add_relation(parent_transform_key, object_transform_key, "Armature Parent"); + } break; } -- cgit v1.2.3 From 235655ee0d0634db9150b1dc266e3a3b35c491e4 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Fri, 3 Sep 2021 17:16:42 +0200 Subject: Improve message in ob_parbone() about the missing Parent Bone - lower to warning (might be debatable, but this is not really malfunctioning and e.g. constraints/modifiers dont spit out errors if targets are not specified) - clarify _what_ of the two actualy does not exist ref. T91101 Maniphest Tasks: T91101 Differential Revision: https://developer.blender.org/D12389 --- source/blender/blenkernel/intern/object.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index c91cf6ed926..062264c5729 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -3307,8 +3307,8 @@ static void ob_parbone(Object *ob, Object *par, float r_mat[4][4]) /* Make sure the bone is still valid */ bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, ob->parsubstr); if (!pchan || !pchan->bone) { - CLOG_ERROR( - &LOG, "Object %s with Bone parent: bone %s doesn't exist", ob->id.name + 2, ob->parsubstr); + CLOG_WARN( + &LOG, "Parent Bone: '%s' for Object: '%s' doesn't exist", ob->parsubstr, ob->id.name + 2); unit_m4(r_mat); return; } -- cgit v1.2.3 From 4fb7217043627ce952583d99c4b8537e10ee2903 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Fri, 3 Sep 2021 13:03:28 -0700 Subject: UDIM: Show the UV grid even when images are loaded Allow the UDIM grid to be shown and adjusted when there are images loaded in UV edit mode. Right now the grid feature disappears once an image is loaded and many have found this to be confusing. Based on community and artist feedback, there was support to change this behavior[1] This patch does the following: - Allows the grid to be shown even when images are present - The max allowable dimensions for the grid has been increased from 10x10 to 10x100 to match the underlying maximum UDIM range that blender supports Note: This should not affect other Image editor modes like Paint/Mask or the Render Result viewer etc. Future work in this area is currently documented in a dedicated design task[2] [1] https://devtalk.blender.org/t/the-udim-tile-grid-design-and-feedback-thread/20136 [2] https://developer.blender.org/T90913 Differential Revision: https://developer.blender.org/D11860 --- release/scripts/startup/bl_ui/space_image.py | 2 +- source/blender/draw/CMakeLists.txt | 1 + source/blender/draw/engines/overlay/overlay_grid.c | 18 ++++++++++++++---- source/blender/draw/engines/overlay/overlay_private.h | 1 + source/blender/draw/engines/overlay/overlay_shader.c | 16 ++++++++++++++++ .../engines/overlay/shaders/grid_background_frag.glsl | 12 ++++++++++++ source/blender/makesrna/intern/rna_space.c | 13 ++++++++++++- 7 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index dcb0ab2e9e5..3ee668888f3 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -1453,7 +1453,7 @@ class IMAGE_PT_udim_grid(Panel): def poll(cls, context): sima = context.space_data - return sima.show_uvedit and sima.image is None + return sima.show_uvedit def draw(self, context): layout = self.layout diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 8bf74dae7f8..71115c5ceb9 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -447,6 +447,7 @@ data_to_c_simple(engines/overlay/shaders/extra_wire_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/extra_wire_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/facing_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/facing_vert.glsl SRC) +data_to_c_simple(engines/overlay/shaders/grid_background_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/grid_frag.glsl SRC) data_to_c_simple(engines/overlay/shaders/grid_vert.glsl SRC) data_to_c_simple(engines/overlay/shaders/image_vert.glsl SRC) diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 5bb157ed081..60cda9f2d61 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -61,10 +61,19 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) if (pd->space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - shd->grid_flag = ED_space_image_has_buffer(sima) ? 0 : PLANE_IMAGE | SHOW_GRID; + if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) { + shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID; + } + else { + shd->grid_flag = 0; + } + shd->grid_distance = 1.0f; - copy_v3_fl3( - shd->grid_size, (float)sima->tile_grid_shape[0], (float)sima->tile_grid_shape[1], 1.0f); + copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f); + if (sima->mode == SI_MODE_UV) { + shd->grid_size[0] = (float)sima->tile_grid_shape[0]; + shd->grid_size[1] = (float)sima->tile_grid_shape[1]; + } for (int step = 0; step < 8; step++) { shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f); } @@ -209,11 +218,12 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) float mat[4][4]; /* add quad background */ - sh = OVERLAY_shader_grid_image(); + sh = OVERLAY_shader_grid_background(); grp = DRW_shgroup_create(sh, psl->grid_ps); float color_back[4]; interp_v4_v4v4(color_back, G_draw.block.colorBackground, G_draw.block.colorGrid, 0.5); DRW_shgroup_uniform_vec4_copy(grp, "color", color_back); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); unit_m4(mat); mat[0][0] = shd->grid_size[0]; mat[1][1] = shd->grid_size[1]; diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 68f60bee779..23df571e8de 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -722,6 +722,7 @@ GPUShader *OVERLAY_shader_extra_point(void); GPUShader *OVERLAY_shader_facing(void); GPUShader *OVERLAY_shader_gpencil_canvas(void); GPUShader *OVERLAY_shader_grid(void); +GPUShader *OVERLAY_shader_grid_background(void); GPUShader *OVERLAY_shader_grid_image(void); GPUShader *OVERLAY_shader_image(void); GPUShader *OVERLAY_shader_motion_path_line(void); diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c index edf9148c8c0..389704b3d66 100644 --- a/source/blender/draw/engines/overlay/overlay_shader.c +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -91,6 +91,7 @@ extern char datatoc_extra_wire_frag_glsl[]; extern char datatoc_extra_wire_vert_glsl[]; extern char datatoc_facing_frag_glsl[]; extern char datatoc_facing_vert_glsl[]; +extern char datatoc_grid_background_frag_glsl[]; extern char datatoc_grid_frag_glsl[]; extern char datatoc_grid_vert_glsl[]; extern char datatoc_image_frag_glsl[]; @@ -198,6 +199,7 @@ typedef struct OVERLAY_Shaders { GPUShader *facing; GPUShader *gpencil_canvas; GPUShader *grid; + GPUShader *grid_background; GPUShader *grid_image; GPUShader *image; GPUShader *motion_path_line; @@ -1053,6 +1055,20 @@ GPUShader *OVERLAY_shader_grid(void) return sh_data->grid; } +GPUShader *OVERLAY_shader_grid_background(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->grid_background) { + sh_data->grid_background = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_view_lib_glsl, + datatoc_edit_uv_tiled_image_borders_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_grid_background_frag_glsl, NULL}, + }); + } + return sh_data->grid_background; +} + GPUShader *OVERLAY_shader_grid_image(void) { OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; diff --git a/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl new file mode 100644 index 00000000000..fcc05414ea3 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl @@ -0,0 +1,12 @@ + +uniform vec4 color; +uniform sampler2D depthBuffer; + +out vec4 fragColor; + +void main() +{ + fragColor = color; + float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + fragColor.a = (scene_depth == 1.0) ? 1.0 : 0.0; +} diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index d391b09189a..7aee6944d8d 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1796,6 +1796,16 @@ static const EnumPropertyItem *rna_SpaceImageEditor_pivot_itemf(bContext *UNUSED } } +static void rna_SpaceUVEditor_tile_grid_shape_set(PointerRNA *ptr, const int *values) +{ + SpaceImage *data = (SpaceImage *)(ptr->data); + + int clamp[2] = {10, 100}; + for (int i = 0; i < 2; i++) { + data->tile_grid_shape[i] = CLAMPIS(values[i], 1, clamp[i]); + } +} + /* Space Text Editor */ static void rna_SpaceTextEditor_word_wrap_set(PointerRNA *ptr, bool value) @@ -3417,7 +3427,8 @@ static void rna_def_space_image_uv(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "tile_grid_shape"); RNA_def_property_array(prop, 2); RNA_def_property_int_default(prop, 1); - RNA_def_property_range(prop, 1, 10); + RNA_def_property_range(prop, 1, 100); + RNA_def_property_int_funcs(prop, NULL, "rna_SpaceUVEditor_tile_grid_shape_set", NULL); RNA_def_property_ui_text( prop, "Tile Grid Shape", "How many tiles will be shown in the background"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); -- cgit v1.2.3 From 716682365c6bcc1b5f757232ce1d2499b0d062a9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 3 Sep 2021 16:48:47 -0500 Subject: Fix T91119: Curve to mesh node inverted face normals Previously I thought I fixed this by reversing the face corner indices in quads created by the curve to mesh node. But then we fixed a problem with the transforms used in that node by inverting one of their components, so the required direction also reversed. This commit reverts rBcf28398471c84 and reverses the default direction of the quadrilateral primitive so it's the same as the others. Tests will be updated. --- .../node_geo_curve_primitive_quadrilateral.cc | 40 +++++++++++----------- .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 12 +++---- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 0d78d6c08d1..07ddaa8f61e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -30,10 +30,10 @@ static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilde b.add_input("Offset").default_value(1.0f).subtype(PROP_DISTANCE); b.add_input("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); b.add_input("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("Point 1").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 2").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 3").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 4").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); + b.add_input("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); b.add_output("Curve"); } @@ -106,10 +106,10 @@ static void create_rectangle_curve(MutableSpan positions, const float height, const float width) { - positions[0] = float3(width / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-width / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-width / 2.0f, height / 2.0f, 0.0f); - positions[3] = float3(width / 2.0f, height / 2.0f, 0.0f); + positions[0] = float3(width / 2.0f, height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f, height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f, -height / 2.0f, 0.0f); } static void create_points_curve(MutableSpan positions, @@ -129,10 +129,10 @@ static void create_parallelogram_curve(MutableSpan positions, const float width, const float offset) { - positions[0] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); - positions[3] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[0] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); } static void create_trapezoid_curve(MutableSpan positions, const float bottom, @@ -140,10 +140,10 @@ static void create_trapezoid_curve(MutableSpan positions, const float offset, const float height) { - positions[0] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); - positions[1] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); - positions[2] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); - positions[3] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); + positions[0] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); + positions[1] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); + positions[2] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); + positions[3] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); } static void create_kite_curve(MutableSpan positions, @@ -151,10 +151,10 @@ static void create_kite_curve(MutableSpan positions, const float bottom_height, const float top_height) { - positions[0] = float3(-width / 2.0f, 0, 0); - positions[1] = float3(0, top_height, 0); - positions[2] = float3(width / 2, 0, 0); - positions[3] = float3(0, -bottom_height, 0); + positions[0] = float3(0, -bottom_height, 0); + positions[1] = float3(width / 2, 0, 0); + positions[2] = float3(0, top_height, 0); + positions[3] = float3(-width / 2.0f, 0, 0); } static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index ebaae59fbd6..b0c763c7d06 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -164,16 +164,16 @@ static void spline_extrude_to_mesh_data(const Spline &spline, MLoop &loop_a = r_loops[ring_segment_loop_offset]; loop_a.v = ring_vert_offset + i_profile; - loop_a.e = spline_edge_start + i_ring; + loop_a.e = ring_edge_start + i_profile; MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; - loop_b.v = next_ring_vert_offset + i_profile; - loop_b.e = next_ring_edge_offset + i_profile; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; loop_c.v = next_ring_vert_offset + i_next_profile; - loop_c.e = next_spline_edge_start + i_ring; + loop_c.e = next_ring_edge_offset + i_profile; MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; - loop_d.v = ring_vert_offset + i_next_profile; - loop_d.e = ring_edge_start + i_profile; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; } } -- cgit v1.2.3 From e6194e735791b42feb51e810a4910a41d999d3bf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 4 Sep 2021 14:22:44 +1000 Subject: RNA: support extracting names from paths without allocating memory Support extracting identifiers RNA paths into fixed size buffer since the maximum size of the identifier is known all cases. - Add BLI_str_unescape_ex to support limiting the destination buffer. - Add BLI_str_quoted_substr to copy values into a fixed size buffer. --- source/blender/blenkernel/intern/action_bones.cc | 6 +- source/blender/blenkernel/intern/fcurve.c | 24 +++--- source/blender/blenlib/BLI_string.h | 12 ++- source/blender/blenlib/intern/string.c | 97 ++++++++++++++++++---- source/blender/editors/animation/anim_deps.c | 7 +- source/blender/editors/animation/anim_filter.c | 25 +++--- source/blender/editors/animation/anim_ipo_utils.c | 30 ++----- .../blender/editors/animation/keyframes_general.c | 9 +- source/blender/editors/animation/keyframing.c | 16 ++-- source/blender/editors/armature/pose_select.c | 8 +- .../editors/transform/transform_convert_armature.c | 8 +- source/blender/io/collada/BCAnimationCurve.cpp | 5 +- source/blender/io/collada/BCAnimationSampler.cpp | 5 +- 13 files changed, 153 insertions(+), 99 deletions(-) diff --git a/source/blender/blenkernel/intern/action_bones.cc b/source/blender/blenkernel/intern/action_bones.cc index b8d185e6a81..1f2b7360b70 100644 --- a/source/blender/blenkernel/intern/action_bones.cc +++ b/source/blender/blenkernel/intern/action_bones.cc @@ -28,6 +28,7 @@ #include "DNA_action_types.h" #include "DNA_anim_types.h" +#include "DNA_armature_types.h" #include "MEM_guardedalloc.h" @@ -36,12 +37,11 @@ namespace blender::bke { void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback) { LISTBASE_FOREACH (FCurve *, fcu, &action->curves) { - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (!bone_name) { + char bone_name[MAXBONENAME]; + if (!BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { continue; } callback(fcu, bone_name); - MEM_freeN(bone_name); } } diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index fb6cd5871f4..8e9c504dcbf 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -346,30 +346,30 @@ int BKE_fcurves_filter(ListBase *dst, ListBase *src, const char *dataPrefix, con return 0; } + const size_t quotedName_size = strlen(dataName) + 1; + char *quotedName = alloca(quotedName_size); + /* Search each F-Curve one by one. */ for (fcu = src->first; fcu; fcu = fcu->next) { /* Check if quoted string matches the path. */ if (fcu->rna_path == NULL) { continue; } - - char *quotedName = BLI_str_quoted_substrN(fcu->rna_path, dataPrefix); - if (quotedName == NULL) { + /* Skipping names longer than `quotedName_size` is OK since we're after an exact match. */ + if (!BLI_str_quoted_substr(fcu->rna_path, dataPrefix, quotedName, quotedName_size)) { + continue; + } + if (!STREQ(quotedName, dataName)) { continue; } /* Check if the quoted name matches the required name. */ - if (STREQ(quotedName, dataName)) { - LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); - - ld->data = fcu; - BLI_addtail(dst, ld); + LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); - matches++; - } + ld->data = fcu; + BLI_addtail(dst, ld); - /* Always free the quoted string, since it needs freeing. */ - MEM_freeN(quotedName); + matches++; } /* Return the number of matches. */ return matches; diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index c20376c42b9..eab39433796 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -65,7 +65,10 @@ bool BLI_str_quoted_substr_range(const char *__restrict str, char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC; - +bool BLI_str_quoted_substr(const char *__restrict str, + const char *__restrict prefix, + char *result, + size_t result_maxlen); char *BLI_str_replaceN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new) ATTR_WARN_UNUSED_RESULT @@ -97,8 +100,15 @@ char *BLI_sprintfN(const char *__restrict format, ...) ATTR_WARN_UNUSED_RESULT size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const size_t dst_maxncpy) ATTR_NONNULL(); +size_t BLI_str_unescape_ex(char *__restrict dst, + const char *__restrict src, + const size_t src_maxncpy, + /* Additional arguments. */ + const size_t dst_maxncpy, + bool *r_is_complete) ATTR_NONNULL(); size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, const size_t src_maxncpy) ATTR_NONNULL(); + const char *BLI_str_escape_find_quote(const char *str) ATTR_NONNULL(); size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL(); diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 0be8700810e..fab5e44de25 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -360,6 +360,27 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si return len; } +BLI_INLINE bool str_unescape_pair(char c_next, char *r_out) +{ +#define CASE_PAIR(value_src, value_dst) \ + case value_src: { \ + *r_out = value_dst; \ + return true; \ + } + switch (c_next) { + CASE_PAIR('"', '"'); /* Quote. */ + CASE_PAIR('\\', '\\'); /* Backslash. */ + CASE_PAIR('t', '\t'); /* Tab. */ + CASE_PAIR('n', '\n'); /* Newline. */ + CASE_PAIR('r', '\r'); /* Carriage return. */ + CASE_PAIR('a', '\a'); /* Bell. */ + CASE_PAIR('b', '\b'); /* Backspace. */ + CASE_PAIR('f', '\f'); /* Form-feed. */ + } +#undef CASE_PAIR + return false; +} + /** * This roughly matches C and Python's string escaping with double quotes - `"`. * @@ -368,31 +389,53 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si * * \param dst: The destination string, at least the size of `strlen(src) + 1`. * \param src: The escaped source string. - * \param dst_maxncpy: The maximum number of bytes allowable to copy. + * \param src_maxncpy: The maximum number of bytes allowable to copy from `src`. + * \param dst_maxncpy: The maximum number of bytes allowable to copy into `dst`. + * \param r_is_complete: Set to true when + */ +size_t BLI_str_unescape_ex(char *__restrict dst, + const char *__restrict src, + const size_t src_maxncpy, + /* Additional arguments to #BLI_str_unescape */ + const size_t dst_maxncpy, + bool *r_is_complete) +{ + size_t len = 0; + bool is_complete = true; + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { + if (UNLIKELY(len == dst_maxncpy)) { + is_complete = false; + break; + } + char c = *src; + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; + } + dst[len++] = c; + } + dst[len] = 0; + *r_is_complete = is_complete; + return len; +} + +/** + * See #BLI_str_unescape_ex doc-string. + * + * This function makes the assumption that `dst` always has + * at least `src_maxncpy` bytes available. + * + * Use #BLI_str_unescape_ex if `dst` has a smaller fixed size. * - * \note This is used for parsing animation paths in blend files. + * \note This is used for parsing animation paths in blend files (runs often). */ size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, const size_t src_maxncpy) { size_t len = 0; - for (size_t i = 0; i < src_maxncpy && (*src != '\0'); i++, src++) { + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { char c = *src; - if (c == '\\') { - char c_next = *(src + 1); - if (((c_next == '"') && ((void)(c = '"'), true)) || /* Quote. */ - ((c_next == '\\') && ((void)(c = '\\'), true)) || /* Backslash. */ - ((c_next == 't') && ((void)(c = '\t'), true)) || /* Tab. */ - ((c_next == 'n') && ((void)(c = '\n'), true)) || /* Newline. */ - ((c_next == 'r') && ((void)(c = '\r'), true)) || /* Carriage return. */ - ((c_next == 'a') && ((void)(c = '\a'), true)) || /* Bell. */ - ((c_next == 'b') && ((void)(c = '\b'), true)) || /* Backspace. */ - ((c_next == 'f') && ((void)(c = '\f'), true))) /* Form-feed. */ - { - i++; - src++; - } + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; } - dst[len++] = c; } dst[len] = 0; @@ -491,6 +534,24 @@ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict return result; } +bool BLI_str_quoted_substr(const char *__restrict str, + const char *__restrict prefix, + char *result, + size_t result_maxlen) +{ + int start_match_ofs, end_match_ofs; + if (!BLI_str_quoted_substr_range(str, prefix, &start_match_ofs, &end_match_ofs)) { + return false; + } + const size_t escaped_len = (size_t)(end_match_ofs - start_match_ofs); + bool is_complete; + BLI_str_unescape_ex(result, str + start_match_ofs, escaped_len, result_maxlen, &is_complete); + if (is_complete == false) { + *result = '\0'; + } + return is_complete; +} + /** * string with all instances of substr_old replaced with substr_new, * Returns a copy of the c-string \a str into a newly #MEM_mallocN'd diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 916d4232f03..97679723d84 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -206,16 +206,17 @@ static void animchan_sync_fcurve_scene(bAnimListElem *ale) BLI_assert(GS(owner_id->name) == ID_SCE); Scene *scene = (Scene *)owner_id; FCurve *fcu = (FCurve *)ale->data; + Sequence *seq = NULL; /* Only affect if F-Curve involves sequence_editor.sequences. */ - char *seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); - if (seq_name == NULL) { + char seq_name[sizeof(seq->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "sequences_all[", seq_name, sizeof(seq_name))) { return; } /* Check if this strip is selected. */ Editing *ed = SEQ_editing_get(scene); - Sequence *seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); + seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); MEM_freeN(seq_name); if (seq == NULL) { diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 104540e2257..b12e0ae5cab 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1061,13 +1061,14 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id if (GS(owner_id->name) == ID_OB) { Object *ob = (Object *)owner_id; - char *bone_name; + bPoseChannel *pchan = NULL; + char bone_name[sizeof(pchan->name)]; /* Only consider if F-Curve involves `pose.bones`. */ - if (fcu->rna_path && (bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["))) { + if (fcu->rna_path && + BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { /* Get bone-name, and check if this bone is selected. */ - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* check whether to continue or skip */ if (pchan && pchan->bone) { @@ -1097,17 +1098,17 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id } else if (GS(owner_id->name) == ID_SCE) { Scene *scene = (Scene *)owner_id; - char *seq_name; + Sequence *seq = NULL; + char seq_name[sizeof(seq->name)]; /* Only consider if F-Curve involves `sequence_editor.sequences`. */ - if (fcu->rna_path && (seq_name = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["))) { + if (fcu->rna_path && + BLI_str_quoted_substr(fcu->rna_path, "sequences_all[", seq_name, sizeof(seq_name))) { /* Get strip name, and check if this strip is selected. */ - Sequence *seq = NULL; Editing *ed = SEQ_editing_get(scene); if (ed) { seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); } - MEM_freeN(seq_name); /* Can only add this F-Curve if it is selected. */ if (ads->filterflag & ADS_FILTER_ONLYSEL) { @@ -1139,14 +1140,14 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id } else if (GS(owner_id->name) == ID_NT) { bNodeTree *ntree = (bNodeTree *)owner_id; - char *node_name; + bNode *node = NULL; + char node_name[sizeof(node->name)]; /* Check for selected nodes. */ - if (fcu->rna_path && (node_name = BLI_str_quoted_substrN(fcu->rna_path, "nodes["))) { - bNode *node = NULL; + if (fcu->rna_path && + (BLI_str_quoted_substr(fcu->rna_path, "nodes[", node_name, sizeof(node_name)))) { /* Get strip name, and check if this strip is selected. */ node = nodeFindNodebyName(ntree, node_name); - MEM_freeN(node_name); /* Can only add this F-Curve if it is selected. */ if (node) { diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index eda87cf1897..33b4882927a 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -106,23 +106,14 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) * - If a pointer just refers to the ID-block, then don't repeat this info * since this just introduces clutter. */ - if (strstr(fcu->rna_path, "bones") && strstr(fcu->rna_path, "constraints")) { - /* perform string 'chopping' to get "Bone Name : Constraint Name" */ - char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); - char *constName = BLI_str_quoted_substrN(fcu->rna_path, "constraints["); + + char pchanName[256], constName[256]; + if (BLI_str_quoted_substr(fcu->rna_path, "bones[", pchanName, sizeof(pchanName)) && + BLI_str_quoted_substr(fcu->rna_path, "constraints[", constName, sizeof(constName))) { /* assemble the string to display in the UI... */ - structname = BLI_sprintfN( - "%s : %s", pchanName ? pchanName : "", constName ? constName : ""); + structname = BLI_sprintfN("%s : %s", pchanName, constName); free_structname = 1; - - /* free the temp names */ - if (pchanName) { - MEM_freeN(pchanName); - } - if (constName) { - MEM_freeN(constName); - } } else if (ptr.data != ptr.owner_id) { PropertyRNA *nameprop = RNA_struct_name_property(ptr.type); @@ -139,18 +130,15 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) * displaying the struct name alone is no meaningful information (and also cannot be * filtered well), same for modifiers. So display strip name alongside as well. */ if (GS(ptr.owner_id->name) == ID_SCE) { - if (BLI_str_startswith(fcu->rna_path, "sequence_editor.sequences_all[\"")) { + char stripname[256]; + if (BLI_str_quoted_substr( + fcu->rna_path, "sequence_editor.sequences_all[", stripname, sizeof(stripname))) { if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") || strstr(fcu->rna_path, ".modifiers[")) { - char *stripname = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all["); - const char *structname_all = BLI_sprintfN( - "%s : %s", stripname ? stripname : "", structname); + const char *structname_all = BLI_sprintfN("%s : %s", stripname, structname); if (free_structname) { MEM_freeN((void *)structname); } - if (stripname) { - MEM_freeN(stripname); - } structname = structname_all; free_structname = 1; } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 9f3fe239113..ec33a42af3b 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -761,11 +761,10 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) if ((aci->id_type == ID_OB) && (((Object *)aci->id)->type == OB_ARMATURE) && aci->rna_path) { Object *ob = (Object *)aci->id; - char *bone_name = BLI_str_quoted_substrN(aci->rna_path, "pose.bones["); - if (bone_name) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); - + bPoseChannel *pchan; + char bone_name[sizeof(pchan->name)]; + if (BLI_str_quoted_substr(aci->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); if (pchan) { aci->is_bone = true; } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 292d665caca..8dc4aed9f0e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2218,11 +2218,11 @@ static int clear_anim_v3d_exec(bContext *C, wmOperator *UNUSED(op)) if (ob->mode & OB_MODE_POSE) { if (fcu->rna_path) { /* Get bone-name, and check if this bone is selected. */ - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (bone_name) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); - + bPoseChannel *pchan = NULL; + char bone_name[sizeof(pchan->name)]; + if (BLI_str_quoted_substr( + fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); /* Delete if bone is selected. */ if ((pchan) && (pchan->bone)) { if (pchan->bone->flag & BONE_SELECTED) { @@ -2323,13 +2323,11 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) bPoseChannel *pchan = NULL; /* Get bone-name, and check if this bone is selected. */ - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (bone_name == NULL) { + char bone_name[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) { continue; } - pchan = BKE_pose_channel_find_name(ob->pose, bone_name); - MEM_freeN(bone_name); /* skip if bone is not selected */ if ((pchan) && (pchan->bone)) { diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 4db8569a7d2..e5b8983af93 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -1106,12 +1106,12 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex for (ksp = ks->paths.first; ksp; ksp = ksp->next) { /* only items related to this object will be relevant */ if ((ksp->id == &ob->id) && (ksp->rna_path != NULL)) { - char *boneName = BLI_str_quoted_substrN(ksp->rna_path, "bones["); - if (boneName == NULL) { + bPoseChannel *pchan = NULL; + char boneName[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(ksp->rna_path, "bones[", boneName, sizeof(boneName))) { continue; } - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName); - MEM_freeN(boneName); + pchan = BKE_pose_channel_find_name(pose, boneName); if (pchan) { /* select if bone is visible and can be affected */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 98e00c20170..8f896512410 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -145,15 +145,15 @@ static void autokeyframe_pose( if (act) { for (fcu = act->curves.first; fcu; fcu = fcu->next) { /* only insert keyframes for this F-Curve if it affects the current bone */ - char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones["); - if (pchanName == NULL) { + char pchan_name[sizeof(pchan->name)]; + if (!BLI_str_quoted_substr(fcu->rna_path, "bones[", pchan_name, sizeof(pchan_name))) { continue; } /* only if bone name matches too... * NOTE: this will do constraints too, but those are ok to do here too? */ - if (STREQ(pchanName, pchan->name)) { + if (STREQ(pchan_name, pchan->name)) { insert_keyframe(bmain, reports, id, @@ -166,8 +166,6 @@ static void autokeyframe_pose( &nla_cache, flag); } - - MEM_freeN(pchanName); } } } diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp index 82d8b6e9ff3..005197c9027 100644 --- a/source/blender/io/collada/BCAnimationCurve.cpp +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -173,10 +173,9 @@ std::string BCAnimationCurve::get_animation_name(Object *ob) const name = ""; } else { - char *boneName = BLI_str_quoted_substrN(fcurve->rna_path, "pose.bones["); - if (boneName) { + char boneName[MAXBONENAME]; + if (BLI_str_quoted_substr(fcurve->rna_path, "pose.bones[", boneName, sizeof(boneName))) { name = id_name(ob) + "_" + std::string(boneName); - MEM_freeN(boneName); } else { name = ""; diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp index d2bde193c0a..953af5adffc 100644 --- a/source/blender/io/collada/BCAnimationSampler.cpp +++ b/source/blender/io/collada/BCAnimationSampler.cpp @@ -447,10 +447,9 @@ void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object * for (; fcu; fcu = fcu->next) { object_type = BC_ANIMATION_TYPE_OBJECT; if (ob->type == OB_ARMATURE) { - char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (boneName) { + char boneName[MAXBONENAME]; + if (BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", boneName, sizeof(boneName))) { object_type = BC_ANIMATION_TYPE_BONE; - MEM_freeN(boneName); } } -- cgit v1.2.3 From 863d8065260c9b508dfbda571c92e66e18551c71 Mon Sep 17 00:00:00 2001 From: Mikkel Gjoel Date: Sat, 4 Sep 2021 22:44:18 +1000 Subject: BMesh: optimize edge & face group calculation This changes the search for unprocessed faces to only search from the previously found face. Local testing on 1.5 million triangle meshes gives a 75x speedup (of the code affected, which is the first half of the work). The former code would traverse all faces of a mesh until a face was found that had not been processed. This ended up being slow mainly because it had to load face-data to determine the state of the flag. Secondarily, the way it iterated and marked the mesh, it would end up traversing all previously processed faces to find and unprocessed one. The same optimization has been made for edge-group calculation. Reviewed By: campbellbarton Ref D12379 --- source/blender/bmesh/intern/bmesh_query.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index cb5764b1c91..795d8829ee7 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -2637,7 +2637,7 @@ int BM_mesh_calc_face_groups(BMesh *bm, STACK_DECLARE(stack); BMIter iter; - BMFace *f; + BMFace *f, *f_next; int i; STACK_INIT(group_array, bm->totface); @@ -2662,6 +2662,8 @@ int BM_mesh_calc_face_groups(BMesh *bm, /* detect groups */ stack = MEM_mallocN(sizeof(*stack) * tot_faces, __func__); + f_next = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); + while (tot_touch != tot_faces) { int *group_item; bool ok = false; @@ -2670,10 +2672,10 @@ int BM_mesh_calc_face_groups(BMesh *bm, STACK_INIT(stack, tot_faces); - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - STACK_PUSH(stack, f); + for (; f_next; f_next = BM_iter_step(&iter)) { + if (BM_elem_flag_test(f_next, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(f_next, BM_ELEM_TAG); + STACK_PUSH(stack, f_next); ok = true; break; } @@ -2799,9 +2801,8 @@ int BM_mesh_calc_edge_groups(BMesh *bm, STACK_DECLARE(stack); BMIter iter; - BMEdge *e; + BMEdge *e, *e_next; int i; - STACK_INIT(group_array, bm->totedge); /* init the array */ @@ -2822,6 +2823,8 @@ int BM_mesh_calc_edge_groups(BMesh *bm, /* detect groups */ stack = MEM_mallocN(sizeof(*stack) * tot_edges, __func__); + e_next = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); + while (tot_touch != tot_edges) { int *group_item; bool ok = false; @@ -2830,10 +2833,10 @@ int BM_mesh_calc_edge_groups(BMesh *bm, STACK_INIT(stack, tot_edges); - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { - BM_elem_flag_enable(e, BM_ELEM_TAG); - STACK_PUSH(stack, e); + for (; e_next; e_next = BM_iter_step(&iter)) { + if (BM_elem_flag_test(e_next, BM_ELEM_TAG) == false) { + BM_elem_flag_enable(e_next, BM_ELEM_TAG); + STACK_PUSH(stack, e_next); ok = true; break; } -- cgit v1.2.3 From c23b6596b91ceaf19d6e98bd66d7e67726821dc2 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Sat, 4 Sep 2021 15:34:01 +0200 Subject: GPencil: Fix subdivision modifier disabled on strokes with 2 points Fixes the regression introduces in rB29f3af952725 . The subdivision modifier used to work on two point strokes with simple mode but not with catmul clark. Now it will work with simple mode and in case of catmull clark mode it will still use simple mode on these strokes. Differential Revision: https://developer.blender.org/D12397 --- source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c index f444103ae3f..ee5ba7e92ca 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -76,16 +76,12 @@ static void deformStroke(GpencilModifierData *md, SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md; bGPdata *gpd = ob->data; - /* It makes sense when adding points to a straight line */ - /* e.g. for creating thickness variation in later modifiers. */ - const int minimum_vert = (mmd->flag & GP_SUBDIV_SIMPLE) ? 2 : 3; - if (!is_stroke_affected_by_modifier(ob, mmd->layername, mmd->material, mmd->pass_index, mmd->layer_pass, - minimum_vert, + 2, gpl, gps, mmd->flag & GP_SUBDIV_INVERT_LAYER, @@ -95,7 +91,10 @@ static void deformStroke(GpencilModifierData *md, return; } - BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, mmd->type); + /* For strokes with less than 3 points, only the Simple Subdivision makes sense. */ + short type = gps->totpoints < 3 ? GP_SUBDIV_SIMPLE : mmd->type; + + BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, type); /* If the stroke is cyclic, must generate the closing geometry. */ if (gps->flag & GP_STROKE_CYCLIC) { -- cgit v1.2.3 From 54b72fe9ff2a7a061423270db8d6853986bbb796 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Sat, 4 Sep 2021 16:02:40 +0200 Subject: GPencil: Fix Noise Modifier Randomize Panel disabled Fix regression introduced in rB34b213d60472 Reviewed By: antoniov Differential Revision: https://developer.blender.org/D12398 --- source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 9e9eba3d61e..ae862ce3eda 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -324,7 +324,7 @@ static void random_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiLayoutSetActive(layout, RNA_boolean_get(ptr, "random")); + uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_random")); uiItemR(layout, ptr, "step", 0, NULL, ICON_NONE); } -- cgit v1.2.3 From 9290b41381fdf02d1431b216de6477f93b2897cb Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sat, 4 Sep 2021 08:00:22 -0700 Subject: Python: Allow Area Close via Scripting Screen area maintenance "Close" function allowed to be scripted. See D12307 for usage example. Differential Revision: https://developer.blender.org/D12307 Reviewed by Campbell Barton --- source/blender/editors/screen/screen_ops.c | 38 ++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index daac196a90c..75f1baf30bb 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1466,15 +1466,39 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) * Close selected area, replace by expanding a neighbor * \{ */ -/* operator callback */ -static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +/** + * \note This can be used interactively or from Python. + * + * \note Most of the window management operators don't support execution from Python. + * An exception is made for closing areas since it allows application templates + * to customize the layout. + */ +static int area_close_exec(bContext *C, wmOperator *op) { + bScreen *screen = CTX_wm_screen(C); ScrArea *area = CTX_wm_area(C); - if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) { - WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); - return OPERATOR_FINISHED; + + /* This operator is scriptable, so the area passed could be invalid. */ + if (BLI_findindex(&screen->areabase, area) == -1) { + BKE_report(op->reports, RPT_ERROR, "Area not found in the active screen"); + return OPERATOR_CANCELLED; } - return OPERATOR_CANCELLED; + + if (!screen_area_close(C, screen, area)) { + BKE_report(op->reports, RPT_ERROR, "Unable to close area"); + return OPERATOR_CANCELLED; + } + + /* Ensure the event loop doesn't attempt to continue handling events. + * + * This causes execution from the Python console fail to return to the prompt as it should. + * This glitch could be solved in the event loop handling as other operators may also + * destructively manipulate windowing data. */ + CTX_wm_window_set(C, NULL); + + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } static bool area_close_poll(bContext *C) @@ -1506,7 +1530,7 @@ static void SCREEN_OT_area_close(wmOperatorType *ot) ot->name = "Close Area"; ot->description = "Close selected area"; ot->idname = "SCREEN_OT_area_close"; - ot->invoke = area_close_invoke; + ot->exec = area_close_exec; ot->poll = area_close_poll; } -- cgit v1.2.3 From 9d7cb5c4a1158266d2f8caa1fc19be2a00fdf101 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 4 Sep 2021 15:23:28 +0200 Subject: Compositor: Full frame filter nodes Adds full frame implementation to Anti-Aliasing, Defocus, Denoise, Despeckle, Dilate/Erode, Directional Blur, Filter, Inpaint and Vector Blur nodes. The other nodes in "Filter" sub-menu are submitted separately. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12219 --- .../blender/compositor/intern/COM_MemoryBuffer.h | 6 + .../COM_ConvertDepthToRadiusOperation.cc | 27 ++ .../operations/COM_ConvertDepthToRadiusOperation.h | 8 +- .../COM_ConvolutionEdgeFilterOperation.cc | 77 +++++ .../COM_ConvolutionEdgeFilterOperation.h | 4 + .../operations/COM_ConvolutionFilterOperation.cc | 58 ++++ .../operations/COM_ConvolutionFilterOperation.h | 13 +- .../compositor/operations/COM_DenoiseOperation.cc | 100 ++++-- .../compositor/operations/COM_DenoiseOperation.h | 15 +- .../operations/COM_DespeckleOperation.cc | 107 +++++++ .../compositor/operations/COM_DespeckleOperation.h | 12 +- .../operations/COM_DilateErodeOperation.cc | 355 ++++++++++++++++++++- .../operations/COM_DilateErodeOperation.h | 40 ++- .../operations/COM_DirectionalBlurOperation.cc | 54 ++++ .../operations/COM_DirectionalBlurOperation.h | 9 +- .../compositor/operations/COM_InpaintOperation.cc | 44 +++ .../compositor/operations/COM_InpaintOperation.h | 7 + .../compositor/operations/COM_SMAAOperation.cc | 355 +++++++++++++++++++-- .../compositor/operations/COM_SMAAOperation.h | 28 +- .../operations/COM_VectorBlurOperation.cc | 46 +++ .../operations/COM_VectorBlurOperation.h | 11 + 21 files changed, 1286 insertions(+), 90 deletions(-) diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index f3e15c2a495..f730d53acec 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -373,6 +373,12 @@ class MemoryBuffer { return this->m_buffer; } + float *release_ownership_buffer() + { + owns_data_ = false; + return this->m_buffer; + } + MemoryBuffer *inflate() const; inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index a9c58b55d73..405ba03abf3 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -116,4 +116,31 @@ void ConvertDepthToRadiusOperation::deinitExecution() this->m_inputOperation = nullptr; } +void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float z = *it.in(0); + if (z == 0.0f) { + *it.out = 0.0f; + continue; + } + + const float inv_z = (1.0f / z); + + /* Bug T6656 part 2b, do not re-scale. */ +#if 0 + bcrad = 0.5f * fabs(aperture * (dof_sp * (cam_invfdist - iZ) - 1.0f)); + /* Scale crad back to original maximum and blend: + * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ +#endif + const float radius = 0.5f * + fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f)); + /* Bug T6615, limit minimum radius to 1 pixel, + * not really a solution, but somewhat mitigates the problem. */ + *it.out = CLAMPIS(radius, 0.0f, m_maxRadius); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index 1f4e856b128..3d163843d06 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -19,7 +19,7 @@ #pragma once #include "COM_FastGaussianBlurOperation.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_object_types.h" namespace blender::compositor { @@ -28,7 +28,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class ConvertDepthToRadiusOperation : public NodeOperation { +class ConvertDepthToRadiusOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -83,6 +83,10 @@ class ConvertDepthToRadiusOperation : public NodeOperation { { this->m_blurPostOperation = operation; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 5ead300a368..9127a871b04 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -95,4 +95,81 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, output[3] = MAX2(output[3], 0.0f); } +void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; + const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; + const int down_offset = (it.y == 0) ? 0 : -image->row_stride; + const int up_offset = (it.y == last_y) ? 0 : image->row_stride; + + const float *center_color = it.in(IMAGE_INPUT_INDEX); + float res1[4] = {0}; + float res2[4] = {0}; + + const float *color = center_color + down_offset + left_offset; + madd_v3_v3fl(res1, color, m_filter[0]); + copy_v3_v3(res2, res1); + + color = center_color + down_offset; + madd_v3_v3fl(res1, color, m_filter[1]); + madd_v3_v3fl(res2, color, m_filter[3]); + + color = center_color + down_offset + right_offset; + madd_v3_v3fl(res1, color, m_filter[2]); + madd_v3_v3fl(res2, color, m_filter[6]); + + color = center_color + left_offset; + madd_v3_v3fl(res1, color, m_filter[3]); + madd_v3_v3fl(res2, color, m_filter[1]); + + { + float rgb_filtered[3]; + mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]); + add_v3_v3(res1, rgb_filtered); + add_v3_v3(res2, rgb_filtered); + } + + color = center_color + right_offset; + madd_v3_v3fl(res1, color, m_filter[5]); + madd_v3_v3fl(res2, color, m_filter[7]); + + color = center_color + up_offset + left_offset; + madd_v3_v3fl(res1, color, m_filter[6]); + madd_v3_v3fl(res2, color, m_filter[2]); + + color = center_color + up_offset; + madd_v3_v3fl(res1, color, m_filter[7]); + madd_v3_v3fl(res2, color, m_filter[5]); + + { + color = center_color + up_offset + right_offset; + float rgb_filtered[3]; + mul_v3_v3fl(rgb_filtered, color, m_filter[8]); + add_v3_v3(res1, rgb_filtered); + add_v3_v3(res2, rgb_filtered); + } + + it.out[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]); + it.out[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]); + it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]); + + const float factor = *it.in(FACTOR_INPUT_INDEX); + const float m_factor = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * m_factor; + it.out[1] = it.out[1] * factor + center_color[1] * m_factor; + it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + + it.out[3] = center_color[3]; + + /* Make sure we don't return negative color. */ + CLAMP4_MIN(it.out, 0.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h index 319b424bd4a..bd38e27165a 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h @@ -25,6 +25,10 @@ namespace blender::compositor { class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation { public: void executePixel(float output[4], int x, int y, void *data) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 72cbbf4283a..11a077229fd 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -127,4 +127,62 @@ bool ConvolutionFilterOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + const int add_x = (m_filterWidth - 1) / 2 + 1; + const int add_y = (m_filterHeight - 1) / 2 + 1; + r_input_area.xmin = output_area.xmin - add_x; + r_input_area.xmax = output_area.xmax + add_x; + r_input_area.ymin = output_area.ymin - add_y; + r_input_area.ymax = output_area.ymax + add_y; + break; + } + case FACTOR_INPUT_INDEX: { + r_input_area = output_area; + break; + } + } +} + +void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; + const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; + const int down_offset = (it.y == 0) ? 0 : -image->row_stride; + const int up_offset = (it.y == last_y) ? 0 : image->row_stride; + + const float *center_color = it.in(IMAGE_INPUT_INDEX); + zero_v4(it.out); + madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]); + madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]); + madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]); + madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]); + madd_v4_v4fl(it.out, center_color, m_filter[4]); + madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]); + madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]); + madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]); + madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]); + + const float factor = *it.in(FACTOR_INPUT_INDEX); + const float m_factor = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * m_factor; + it.out[1] = it.out[1] * factor + center_color[1] * m_factor; + it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + it.out[3] = it.out[3] * factor + center_color[3] * m_factor; + + /* Make sure we don't return negative color. */ + CLAMP4_MIN(it.out, 0.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h index 16dee502929..7e12c7faa5c 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h @@ -18,11 +18,15 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class ConvolutionFilterOperation : public NodeOperation { +class ConvolutionFilterOperation : public MultiThreadedOperation { + protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int FACTOR_INPUT_INDEX = 1; + private: int m_filterWidth; int m_filterHeight; @@ -43,6 +47,11 @@ class ConvolutionFilterOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index ec11ad4d69a..e7f2d5a740a 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -35,6 +35,8 @@ DenoiseOperation::DenoiseOperation() this->addInputSocket(DataType::Color); this->addOutputSocket(DataType::Color); this->m_settings = nullptr; + flags.is_fullframe_operation = true; + output_rendered_ = false; } void DenoiseOperation::initExecution() { @@ -63,8 +65,7 @@ MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) rect.xmax = getWidth(); rect.ymax = getHeight(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - float *data = result->getBuffer(); - this->generateDenoise(data, tileColor, tileNormal, tileAlbedo, this->m_settings); + this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings); return result; } @@ -84,23 +85,33 @@ bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -void DenoiseOperation::generateDenoise(float *data, - MemoryBuffer *inputTileColor, - MemoryBuffer *inputTileNormal, - MemoryBuffer *inputTileAlbedo, +void DenoiseOperation::generateDenoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, NodeDenoise *settings) { - float *inputBufferColor = inputTileColor->getBuffer(); - BLI_assert(inputBufferColor); - if (!inputBufferColor) { + BLI_assert(input_color->getBuffer()); + if (!input_color->getBuffer()) { return; } + #ifdef WITH_OPENIMAGEDENOISE /* Always supported through Accelerate framework BNNS on macOS. */ # ifndef __APPLE__ if (BLI_cpu_support_sse41()) # endif { + /* OpenImageDenoise needs full buffers. */ + MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : + input_color; + MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? + input_normal->inflate() : + input_normal; + MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? + input_albedo->inflate() : + input_albedo; + /* Since it's memory intensive, it's better to run only one instance of OIDN at a time. * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless. */ @@ -111,35 +122,35 @@ void DenoiseOperation::generateDenoise(float *data, oidn::FilterRef filter = device.newFilter("RT"); filter.setImage("color", - inputBufferColor, + buf_color->getBuffer(), oidn::Format::Float3, - inputTileColor->getWidth(), - inputTileColor->getHeight(), + buf_color->getWidth(), + buf_color->getHeight(), 0, sizeof(float[4])); - if (inputTileNormal && inputTileNormal->getBuffer()) { + if (buf_normal && buf_normal->getBuffer()) { filter.setImage("normal", - inputTileNormal->getBuffer(), + buf_normal->getBuffer(), oidn::Format::Float3, - inputTileNormal->getWidth(), - inputTileNormal->getHeight(), + buf_normal->getWidth(), + buf_normal->getHeight(), 0, sizeof(float[3])); } - if (inputTileAlbedo && inputTileAlbedo->getBuffer()) { + if (buf_albedo && buf_albedo->getBuffer()) { filter.setImage("albedo", - inputTileAlbedo->getBuffer(), + buf_albedo->getBuffer(), oidn::Format::Float3, - inputTileAlbedo->getWidth(), - inputTileAlbedo->getHeight(), + buf_albedo->getWidth(), + buf_albedo->getHeight(), 0, sizeof(float[4])); } filter.setImage("output", - data, + output->getBuffer(), oidn::Format::Float3, - inputTileColor->getWidth(), - inputTileColor->getHeight(), + buf_color->getWidth(), + buf_color->getHeight(), 0, sizeof(float[4])); @@ -153,19 +164,46 @@ void DenoiseOperation::generateDenoise(float *data, filter.execute(); BLI_mutex_unlock(&oidn_lock); - /* copy the alpha channel, OpenImageDenoise currently only supports RGB */ - size_t numPixels = inputTileColor->getWidth() * inputTileColor->getHeight(); - for (size_t i = 0; i < numPixels; i++) { - data[i * 4 + 3] = inputBufferColor[i * 4 + 3]; + /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ + output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); + + /* Delete inflated buffers. */ + if (input_color->is_a_single_elem()) { + delete buf_color; + } + if (input_normal && input_normal->is_a_single_elem()) { + delete buf_normal; } + if (input_albedo && input_albedo->is_a_single_elem()) { + delete buf_albedo; + } + return; } #endif /* If built without OIDN or running on an unsupported CPU, just pass through. */ - UNUSED_VARS(inputTileAlbedo, inputTileNormal, settings); - ::memcpy(data, - inputBufferColor, - sizeof(float[4]) * inputTileColor->getWidth() * inputTileColor->getHeight()); + UNUSED_VARS(input_albedo, input_normal, settings); + output->copy_from(input_color, input_color->get_rect()); +} + +void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span inputs) +{ + if (!output_rendered_) { + this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings); + output_rendered_ = true; + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index a9298c17e92..48209c3eacf 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -37,6 +37,8 @@ class DenoiseOperation : public SingleThreadedOperation { */ NodeDenoise *m_settings; + bool output_rendered_; + public: DenoiseOperation(); /** @@ -57,11 +59,16 @@ class DenoiseOperation : public SingleThreadedOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + protected: - void generateDenoise(float *data, - MemoryBuffer *inputTileColor, - MemoryBuffer *inputTileNormal, - MemoryBuffer *inputTileAlbedo, + void generateDenoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, NodeDenoise *settings); MemoryBuffer *createMemoryBuffer(rcti *rect) override; diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index fc8778c7d2e..19bd7b2af6f 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -127,6 +127,11 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da else { copy_v4_v4(output, color_org); } + +#undef TOT_DIV_ONE +#undef TOT_DIV_CNR +#undef WTOT +#undef COLOR_ADD } bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, @@ -144,4 +149,106 @@ bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DespeckleOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + switch (input_idx) { + case IMAGE_INPUT_INDEX: { + const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1; + const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1; + r_input_area.xmin = output_area.xmin - add_x; + r_input_area.xmax = output_area.xmax + add_x; + r_input_area.ymin = output_area.ymin - add_y; + r_input_area.ymax = output_area.ymax + add_y; + break; + } + case FACTOR_INPUT_INDEX: { + r_input_area = output_area; + break; + } + } +} + +void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const int last_x = getWidth() - 1; + const int last_y = getHeight() - 1; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const int x1 = MAX2(it.x - 1, 0); + const int x2 = it.x; + const int x3 = MIN2(it.x + 1, last_x); + const int y1 = MAX2(it.y - 1, 0); + const int y2 = it.y; + const int y3 = MIN2(it.y + 1, last_y); + + float w = 0.0f; + const float *color_org = it.in(IMAGE_INPUT_INDEX); + float color_mid[4]; + float color_mid_ok[4]; + const float *in1 = nullptr; + +#define TOT_DIV_ONE 1.0f +#define TOT_DIV_CNR (float)M_SQRT1_2 + +#define WTOT (TOT_DIV_ONE * 4 + TOT_DIV_CNR * 4) + +#define COLOR_ADD(fac) \ + { \ + madd_v4_v4fl(color_mid, in1, fac); \ + if (color_diff(in1, color_org, m_threshold)) { \ + w += fac; \ + madd_v4_v4fl(color_mid_ok, in1, fac); \ + } \ + } + + zero_v4(color_mid); + zero_v4(color_mid_ok); + + in1 = image->get_elem(x1, y1); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x2, y1); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x3, y1); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x1, y2); + COLOR_ADD(TOT_DIV_ONE) + +#if 0 + const float* in2 = image->get_elem(x2, y2); + madd_v4_v4fl(color_mid, in2, this->m_filter[4]); +#endif + + in1 = image->get_elem(x3, y2); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x1, y3); + COLOR_ADD(TOT_DIV_CNR) + in1 = image->get_elem(x2, y3); + COLOR_ADD(TOT_DIV_ONE) + in1 = image->get_elem(x3, y3); + COLOR_ADD(TOT_DIV_CNR) + + mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); + // mul_v4_fl(color_mid, 1.0f / w); + + if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && + color_diff(color_mid, color_org, m_threshold)) { + const float factor = *it.in(FACTOR_INPUT_INDEX); + mul_v4_fl(color_mid_ok, 1.0f / w); + interp_v4_v4v4(it.out, color_org, color_mid_ok, factor); + } + else { + copy_v4_v4(it.out, color_org); + } + +#undef TOT_DIV_ONE +#undef TOT_DIV_CNR +#undef WTOT +#undef COLOR_ADD + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index e8d3461d2ec..70d6c2227f4 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -18,12 +18,15 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DespeckleOperation : public NodeOperation { +class DespeckleOperation : public MultiThreadedOperation { private: + constexpr static int IMAGE_INPUT_INDEX = 0; + constexpr static int FACTOR_INPUT_INDEX = 1; + float m_threshold; float m_threshold_neighbor; @@ -52,6 +55,11 @@ class DespeckleOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index c459d09f02c..28b40021cd9 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -35,9 +35,9 @@ DilateErodeThresholdOperation::DilateErodeThresholdOperation() this->m__switch = 0.5f; this->m_distance = 0.0f; } -void DilateErodeThresholdOperation::initExecution() + +void DilateErodeThresholdOperation::init_data() { - this->m_inputProgram = this->getInputSocketReader(0); if (this->m_distance < 0.0f) { this->m_scope = -this->m_distance + this->m_inset; } @@ -54,6 +54,11 @@ void DilateErodeThresholdOperation::initExecution() } } +void DilateErodeThresholdOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); +} + void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) { void *buffer = this->m_inputProgram->initializeTileData(nullptr); @@ -160,6 +165,112 @@ bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_scope; + r_input_area.xmax = output_area.xmax + m_scope; + r_input_area.ymin = output_area.ymin - m_scope; + r_input_area.ymax = output_area.ymax + m_scope; +} + +struct DilateErodeThresholdOperation::PixelData { + int x; + int y; + int xmin; + int xmax; + int ymin; + int ymax; + const float *elem; + float distance; + int elem_stride; + int row_stride; + /** Switch. */ + float sw; +}; + +template typename TCompare> +static float get_min_distance(DilateErodeThresholdOperation::PixelData &p) +{ + /* TODO(manzanilla): bad performance, generate a table with relative offsets on operation + * initialization to loop from less to greater distance and break as soon as #compare is + * true. */ + const TCompare compare; + float min_dist = p.distance; + const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride + + ((intptr_t)p.xmin - p.x) * p.elem_stride; + for (int yi = p.ymin; yi < p.ymax; yi++) { + const float dy = yi - p.y; + const float dist_y = dy * dy; + const float *elem = row; + for (int xi = p.xmin; xi < p.xmax; xi++) { + if (compare(*elem, p.sw)) { + const float dx = xi - p.x; + const float dist = dx * dx + dist_y; + min_dist = MIN2(min_dist, dist); + } + elem += p.elem_stride; + } + row += p.row_stride; + } + return min_dist; +} + +void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *input = inputs[0]; + const rcti &input_rect = input->get_rect(); + const float rd = m_scope * m_scope; + const float inset = m_inset; + + PixelData p; + p.sw = m__switch; + p.distance = rd * 2; + p.elem_stride = input->elem_stride; + p.row_stride = input->row_stride; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.x = it.x; + p.y = it.y; + p.xmin = MAX2(p.x - m_scope, input_rect.xmin); + p.ymin = MAX2(p.y - m_scope, input_rect.ymin); + p.xmax = MIN2(p.x + m_scope, input_rect.xmax); + p.ymax = MIN2(p.y + m_scope, input_rect.ymax); + p.elem = it.in(0); + + float pixel_value; + if (*p.elem > p.sw) { + pixel_value = -sqrtf(get_min_distance(p)); + } + else { + pixel_value = sqrtf(get_min_distance(p)); + } + + if (m_distance > 0.0f) { + const float delta = m_distance - pixel_value; + if (delta >= 0.0f) { + *it.out = delta >= inset ? 1.0f : delta / inset; + } + else { + *it.out = 0.0f; + } + } + else { + const float delta = -m_distance + pixel_value; + if (delta < 0.0f) { + *it.out = delta < -inset ? 1.0f : (-delta) / inset; + } + else { + *it.out = 0.0f; + } + } + } +} + /* Dilate Distance. */ DilateDistanceOperation::DilateDistanceOperation() { @@ -170,15 +281,20 @@ DilateDistanceOperation::DilateDistanceOperation() flags.complex = true; flags.open_cl = true; } -void DilateDistanceOperation::initExecution() + +void DilateDistanceOperation::init_data() { - this->m_inputProgram = this->getInputSocketReader(0); this->m_scope = this->m_distance; if (this->m_scope < 3) { this->m_scope = 3; } } +void DilateDistanceOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); +} + void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) { void *buffer = this->m_inputProgram->initializeTileData(nullptr); @@ -258,6 +374,92 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); } +void DilateDistanceOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_scope; + r_input_area.xmax = output_area.xmax + m_scope; + r_input_area.ymin = output_area.ymin - m_scope; + r_input_area.ymax = output_area.ymax + m_scope; +} + +struct DilateDistanceOperation::PixelData { + int x; + int y; + int xmin; + int xmax; + int ymin; + int ymax; + const float *elem; + float min_distance; + int scope; + int elem_stride; + int row_stride; + const rcti &input_rect; + + PixelData(MemoryBuffer *input, const int distance, const int scope) + : min_distance(distance * distance), + scope(scope), + elem_stride(input->elem_stride), + row_stride(input->row_stride), + input_rect(input->get_rect()) + { + } + + void update(BuffersIterator &it) + { + x = it.x; + y = it.y; + xmin = MAX2(x - scope, input_rect.xmin); + ymin = MAX2(y - scope, input_rect.ymin); + xmax = MIN2(x + scope, input_rect.xmax); + ymax = MIN2(y + scope, input_rect.ymax); + elem = it.in(0); + } +}; + +template typename TCompare> +static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value) +{ + /* TODO(manzanilla): bad performance, only loop elements within minimum distance removing + * coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */ + const TCompare compare; + const float min_dist = p.min_distance; + float value = start_value; + const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride + + ((intptr_t)p.xmin - p.x) * p.elem_stride; + for (int yi = p.ymin; yi < p.ymax; yi++) { + const float dy = yi - p.y; + const float dist_y = dy * dy; + const float *elem = row; + for (int xi = p.xmin; xi < p.xmax; xi++) { + const float dx = xi - p.x; + const float dist = dx * dx + dist_y; + if (dist <= min_dist) { + value = compare(*elem, value) ? *elem : value; + } + elem += p.elem_stride; + } + row += p.row_stride; + } + + return value; +} + +void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + PixelData p(inputs[0], m_distance, m_scope); + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.update(it); + *it.out = get_distance_value(p, 0.0f); + } +} + /* Erode Distance */ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() { @@ -318,6 +520,17 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); } +void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + PixelData p(inputs[0], m_distance, m_scope); + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + p.update(it); + *it.out = get_distance_value(p, 1.0f); + } +} + /* Dilate step */ DilateStepOperation::DilateStepOperation() { @@ -475,6 +688,126 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DilateStepOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = output_area.xmin - m_iterations; + r_input_area.xmax = output_area.xmax + m_iterations; + r_input_area.ymin = output_area.ymin - m_iterations; + r_input_area.ymax = output_area.ymax + m_iterations; +} + +template +static void step_update_memory_buffer(MemoryBuffer *output, + const MemoryBuffer *input, + const rcti &area, + const int num_iterations, + const float compare_min_value) +{ + TCompareSelector selector; + + const int width = output->getWidth(); + const int height = output->getHeight(); + + const int half_window = num_iterations; + const int window = half_window * 2 + 1; + + const int xmin = MAX2(0, area.xmin - half_window); + const int ymin = MAX2(0, area.ymin - half_window); + const int xmax = MIN2(width, area.xmax + half_window); + const int ymax = MIN2(height, area.ymax + half_window); + + const int bwidth = area.xmax - area.xmin; + const int bheight = area.ymax - area.ymin; + + /* NOTE: #result has area width, but new height. + * We have to calculate the additional rows in the first pass, + * to have valid data available for the second pass. */ + rcti result_area; + BLI_rcti_init(&result_area, area.xmin, area.xmax, ymin, ymax); + MemoryBuffer result(DataType::Value, result_area); + + /* #temp holds maxima for every step in the algorithm, #buf holds a + * single row or column of input values, padded with #limit values to + * simplify the logic. */ + float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp"); + float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window), + "dilate erode buf"); + + /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */ + /* First pass, horizontal dilate/erode. */ + for (int y = ymin; y < ymax; y++) { + for (int x = 0; x < bwidth + 5 * half_window; x++) { + buf[x] = compare_min_value; + } + for (int x = xmin; x < xmax; x++) { + buf[x - area.xmin + window - 1] = input->get_value(x, y, 0); + } + + for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) { + int start = (i + 1) * window - 1; + + temp[window - 1] = buf[start]; + for (int x = 1; x < window; x++) { + temp[window - 1 - x] = selector(temp[window - x], buf[start - x]); + temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]); + } + + start = half_window + (i - 1) * window + 1; + for (int x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) { + result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]); + } + } + } + + /* Second pass, vertical dilate/erode. */ + for (int x = 0; x < bwidth; x++) { + for (int y = 0; y < bheight + 5 * half_window; y++) { + buf[y] = compare_min_value; + } + for (int y = ymin; y < ymax; y++) { + buf[y - area.ymin + window - 1] = result.get_value(x + area.xmin, y, 0); + } + + for (int i = 0; i < (bheight + 3 * half_window) / window; i++) { + int start = (i + 1) * window - 1; + + temp[window - 1] = buf[start]; + for (int y = 1; y < window; y++) { + temp[window - 1 - y] = selector(temp[window - y], buf[start - y]); + temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]); + } + + start = half_window + (i - 1) * window + 1; + for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) { + result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]); + } + } + } + + MEM_freeN(temp); + MEM_freeN(buf); + + output->copy_from(&result, area); +} + +struct Max2Selector { + float operator()(float f1, float f2) const + { + return MAX2(f1, f2); + } +}; + +void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + step_update_memory_buffer(output, inputs[0], area, m_iterations, -FLT_MAX); +} + /* Erode step */ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() { @@ -571,4 +904,18 @@ void *ErodeStepOperation::initializeTileData(rcti *rect) return result; } +struct Min2Selector { + float operator()(float f1, float f2) const + { + return MIN2(f1, f2); + } +}; + +void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + step_update_memory_buffer(output, inputs[0], area, m_iterations, FLT_MAX); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index a489e293e8e..9c32a5ac1fd 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -18,11 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DilateErodeThresholdOperation : public NodeOperation { +class DilateErodeThresholdOperation : public MultiThreadedOperation { + public: + struct PixelData; + private: /** * Cached reference to the inputProgram @@ -47,6 +50,7 @@ class DilateErodeThresholdOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -74,10 +78,17 @@ class DilateErodeThresholdOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; -class DilateDistanceOperation : public NodeOperation { - private: +class DilateDistanceOperation : public MultiThreadedOperation { + public: + struct PixelData; + protected: /** * Cached reference to the inputProgram @@ -94,6 +105,7 @@ class DilateDistanceOperation : public NodeOperation { */ void executePixel(float output[4], int x, int y, void *data) override; + void init_data() override; /** * Initialize the execution */ @@ -119,7 +131,13 @@ class DilateDistanceOperation : public NodeOperation { MemoryBuffer **inputMemoryBuffers, std::list *clMemToCleanUp, std::list *clKernelsToCleanUp) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; + class ErodeDistanceOperation : public DilateDistanceOperation { public: ErodeDistanceOperation(); @@ -135,9 +153,13 @@ class ErodeDistanceOperation : public DilateDistanceOperation { MemoryBuffer **inputMemoryBuffers, std::list *clMemToCleanUp, std::list *clKernelsToCleanUp) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; -class DilateStepOperation : public NodeOperation { +class DilateStepOperation : public MultiThreadedOperation { protected: /** * Cached reference to the inputProgram @@ -174,6 +196,11 @@ class DilateStepOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; class ErodeStepOperation : public DilateStepOperation { @@ -181,6 +208,9 @@ class ErodeStepOperation : public DilateStepOperation { ErodeStepOperation(); void *initializeTileData(rcti *rect) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 97bdc25af3b..102025ed915 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -146,4 +146,58 @@ bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/ return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void DirectionalBlurOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *input = inputs[0]; + const int iterations = pow(2.0f, this->m_data->iter); + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + float color_accum[4]; + input->read_elem_bilinear(x, y, color_accum); + + /* Blur pixel. */ + /* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a + * table on operation initialization. */ + float ltx = this->m_tx; + float lty = this->m_ty; + float lsc = this->m_sc; + float lrot = this->m_rot; + for (int i = 0; i < iterations; i++) { + const float cs = cosf(lrot), ss = sinf(lrot); + const float isc = 1.0f / (1.0f + lsc); + + const float v = isc * (y - this->m_center_y_pix) + lty; + const float u = isc * (x - this->m_center_x_pix) + ltx; + + float color[4]; + input->read_elem_bilinear( + cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color); + add_v4_v4(color_accum, color); + + /* Double transformations. */ + ltx += this->m_tx; + lty += this->m_ty; + lrot += this->m_rot; + lsc += this->m_sc; + } + + mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1)); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 5555520462b..9a982bf6481 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -18,12 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "COM_QualityStepHelper.h" namespace blender::compositor { -class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper { +class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: SocketReader *m_inputProgram; NodeDBlurData *m_data; @@ -65,6 +65,11 @@ class DirectionalBlurOperation : public NodeOperation, public QualityStepHelper MemoryBuffer **inputMemoryBuffers, std::list *clMemToCleanUp, std::list *clKernelsToCleanUp) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index bfcd504177f..5e76c41752c 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -39,6 +39,7 @@ InpaintSimpleOperation::InpaintSimpleOperation() this->m_manhattan_distance = nullptr; this->m_cached_buffer = nullptr; this->m_cached_buffer_ready = false; + flags.is_fullframe_operation = true; } void InpaintSimpleOperation::initExecution() { @@ -286,4 +287,47 @@ bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void InpaintSimpleOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ + MemoryBuffer *input = inputs[0]; + if (!m_cached_buffer_ready) { + if (input->is_a_single_elem()) { + MemoryBuffer *tmp = input->inflate(); + m_cached_buffer = tmp->release_ownership_buffer(); + delete tmp; + } + else { + m_cached_buffer = (float *)MEM_dupallocN(input->getBuffer()); + } + + this->calc_manhattan_distance(); + + int curr = 0; + int x, y; + while (this->next_pixel(x, y, curr, this->m_iterations)) { + this->pix_step(x, y); + } + m_cached_buffer_ready = true; + } + + const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); + MemoryBuffer buf(m_cached_buffer, num_channels, input->getWidth(), input->getHeight()); + output->copy_from(&buf, area); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index e3d27bf7704..e11610bd263 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -66,6 +66,13 @@ class InpaintSimpleOperation : public NodeOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + private: void calc_manhattan_distance(); void clamp_xy(int &x, int &y); diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index b078d85372d..4153b9c8523 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -61,6 +61,8 @@ namespace blender::compositor { /*-----------------------------------------------------------------------------*/ /* Internal Functions to Sample Pixel Color from Image */ +/* TODO(manzanilla): to be removed with tiled implementation. Replace it with + * #buffer->read_elem_checked. */ static inline void sample(SocketReader *reader, int x, int y, float color[4]) { if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { @@ -71,8 +73,13 @@ static inline void sample(SocketReader *reader, int x, int y, float color[4]) reader->read(color, x, y, nullptr); } -static void sample_bilinear_vertical( - SocketReader *reader, int x, int y, float yoffset, float color[4]) +static inline void sample(MemoryBuffer *reader, int x, int y, float color[4]) +{ + reader->read_elem_checked(x, y, color); +} + +template +static void sample_bilinear_vertical(T *reader, int x, int y, float yoffset, float color[4]) { float iy = floorf(yoffset); float fy = yoffset - iy; @@ -89,8 +96,8 @@ static void sample_bilinear_vertical( color[3] = interpf(color01[3], color00[3], fy); } -static void sample_bilinear_horizontal( - SocketReader *reader, int x, int y, float xoffset, float color[4]) +template +static void sample_bilinear_horizontal(T *reader, int x, int y, float xoffset, float color[4]) { float ix = floorf(xoffset); float fx = xoffset - ix; @@ -162,7 +169,7 @@ static void area_diag(int d1, int d2, int e1, int e2, float weights[2]) SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() { this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Value); /* depth, material ID, etc. */ + this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ this->addOutputSocket(DataType::Color); this->flags.complex = true; this->m_imageReader = nullptr; @@ -207,6 +214,16 @@ bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area.xmax = output_area.xmax + 1; + r_input_area.xmin = output_area.xmin - 2; + r_input_area.ymax = output_area.ymax + 1; + r_input_area.ymin = output_area.ymin - 2; +} + void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) { float color[4]; @@ -288,6 +305,94 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi } } +void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const MemoryBuffer *image = inputs[0]; + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { + float color[4]; + const int x = it.x; + const int y = it.y; + + /* Calculate luma deltas: */ + image->read_elem_checked(x, y, color); + const float L = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x - 1, y, color); + const float Lleft = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x, y - 1, color); + const float Ltop = IMB_colormanagement_get_luminance(color); + const float Dleft = fabsf(L - Lleft); + const float Dtop = fabsf(L - Ltop); + + /* We do the usual threshold: */ + it.out[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; + it.out[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + it.out[2] = 0.0f; + it.out[3] = 1.0f; + + /* Then discard if there is no edge: */ + if (is_zero_v2(it.out)) { + continue; + } + + /* Calculate right and bottom deltas: */ + image->read_elem_checked(x + 1, y, color); + const float Lright = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x, y + 1, color); + const float Lbottom = IMB_colormanagement_get_luminance(color); + const float Dright = fabsf(L - Lright); + const float Dbottom = fabsf(L - Lbottom); + + /* Calculate the maximum delta in the direct neighborhood: */ + float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + + /* Calculate luma used for both left and top edges: */ + image->read_elem_checked(x - 1, y - 1, color); + const float Llefttop = IMB_colormanagement_get_luminance(color); + + /* Left edge */ + if (it.out[0] != 0.0f) { + /* Calculate deltas around the left pixel: */ + image->read_elem_checked(x - 2, y, color); + const float Lleftleft = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x - 1, y + 1, color); + const float Lleftbottom = IMB_colormanagement_get_luminance(color); + const float Dleftleft = fabsf(Lleft - Lleftleft); + const float Dlefttop = fabsf(Lleft - Llefttop); + const float Dleftbottom = fabsf(Lleft - Lleftbottom); + + /* Calculate the final maximum delta: */ + maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + + /* Local contrast adaptation: */ + if (maxDelta > m_contrast_limit * Dleft) { + it.out[0] = 0.0f; + } + } + + /* Top edge */ + if (it.out[1] != 0.0f) { + /* Calculate top-top delta: */ + image->read_elem_checked(x, y - 2, color); + const float Ltoptop = IMB_colormanagement_get_luminance(color); + image->read_elem_checked(x + 1, y - 1, color); + const float Ltopright = IMB_colormanagement_get_luminance(color); + const float Dtoptop = fabsf(Ltop - Ltoptop); + const float Dtopleft = fabsf(Ltop - Llefttop); + const float Dtopright = fabsf(Ltop - Ltopright); + + /* Calculate the final maximum delta: */ + maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + + /* Local contrast adaptation: */ + if (maxDelta > m_contrast_limit * Dtop) { + it.out[1] = 0.0f; + } + } + } +} + /*-----------------------------------------------------------------------------*/ /* Blending Weight Calculation (Second Pass) */ /*-----------------------------------------------------------------------------*/ @@ -309,6 +414,9 @@ void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) void SMAABlendingWeightCalculationOperation::initExecution() { this->m_imageReader = this->getInputSocketReader(0); + if (execution_model_ == eExecutionModel::Tiled) { + sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; + } } void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) @@ -414,6 +522,113 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], } } +void SMAABlendingWeightCalculationOperation::update_memory_buffer_started( + MemoryBuffer *UNUSED(output), const rcti &UNUSED(out_area), Span inputs) +{ + const MemoryBuffer *image = inputs[0]; + sample_image_fn_ = [=](int x, int y, float *out) { image->read_elem_checked(x, y, out); }; +} + +void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( + MemoryBuffer *output, const rcti &out_area, Span UNUSED(inputs)) +{ + for (BuffersIterator it = output->iterate_with({}, out_area); !it.is_end(); ++it) { + const int x = it.x; + const int y = it.y; + zero_v4(it.out); + + float edges[4]; + sample_image_fn_(x, y, edges); + + /* Edge at north */ + float c[4]; + if (edges[1] > 0.0f) { + /* Diagonals have both north and west edges, so calculating weights for them */ + /* in one of the boundaries is enough. */ + calculateDiagWeights(x, y, edges, it.out); + + /* We give priority to diagonals, so if we find a diagonal we skip. */ + /* horizontal/vertical processing. */ + if (!is_zero_v2(it.out)) { + continue; + } + + /* Find the distance to the left and the right: */ + int left = searchXLeft(x, y); + int right = searchXRight(x, y); + int d1 = x - left, d2 = right - x; + + /* Fetch the left and right crossing edges: */ + int e1 = 0, e2 = 0; + sample_image_fn_(left, y - 1, c); + if (c[0] > 0.0) { + e1 += 1; + } + sample_image_fn_(left, y, c); + if (c[0] > 0.0) { + e1 += 2; + } + sample_image_fn_(right + 1, y - 1, c); + if (c[0] > 0.0) { + e2 += 1; + } + sample_image_fn_(right + 1, y, c); + if (c[0] > 0.0) { + e2 += 2; + } + + /* Ok, we know how this pattern looks like, now it is time for getting */ + /* the actual area: */ + area(d1, d2, e1, e2, it.out); /* R, G */ + + /* Fix corners: */ + if (m_corner_rounding) { + detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); + } + } + + /* Edge at west */ + if (edges[0] > 0.0f) { + /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ + if (isVerticalSearchUnneeded(x, y)) { + continue; + } + + /* Find the distance to the top and the bottom: */ + int top = searchYUp(x, y); + int bottom = searchYDown(x, y); + int d1 = y - top, d2 = bottom - y; + + /* Fetch the top and bottom crossing edges: */ + int e1 = 0, e2 = 0; + sample_image_fn_(x - 1, top, c); + if (c[1] > 0.0) { + e1 += 1; + } + sample_image_fn_(x, top, c); + if (c[1] > 0.0) { + e1 += 2; + } + sample_image_fn_(x - 1, bottom + 1, c); + if (c[1] > 0.0) { + e2 += 1; + } + sample_image_fn_(x, bottom + 1, c); + if (c[1] > 0.0) { + e2 += 2; + } + + /* Get the area for this direction: */ + area(d1, d2, e1, e2, it.out + 2); /* B, A */ + + /* Fix corners: */ + if (m_corner_rounding) { + detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); + } + } + } +} + void SMAABlendingWeightCalculationOperation::deinitExecution() { this->m_imageReader = nullptr; @@ -434,6 +649,19 @@ bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area.xmax = output_area.xmax + + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); + r_input_area.xmin = output_area.xmin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); + r_input_area.ymax = output_area.ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); + r_input_area.ymin = output_area.ymin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); +} + /*-----------------------------------------------------------------------------*/ /* Diagonal Search Functions */ @@ -449,7 +677,7 @@ int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, b while (x != end) { x += dir; y -= dir; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { *found = true; break; @@ -472,12 +700,12 @@ int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, b while (x != end) { x += dir; y += dir; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { *found = true; break; } - sample(m_imageReader, x + 1, y, e); + sample_image_fn_(x + 1, y, e); if (e[0] == 0.0f) { *found = true; return (dir > 0) ? x : x - dir; @@ -522,11 +750,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int left = x - d1, bottom = y + d1; - sample(m_imageReader, left - 1, bottom, c); + sample_image_fn_(left - 1, bottom, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, left, bottom, c); + sample_image_fn_(left, bottom, c); if (c[0] > 0.0) { e1 += 1; } @@ -536,11 +764,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int right = x + d2, top = y - d2; - sample(m_imageReader, right + 1, top, c); + sample_image_fn_(right + 1, top, c); if (c[1] > 0.0) { e2 += 2; } - sample(m_imageReader, right + 1, top - 1, c); + sample_image_fn_(right + 1, top - 1, c); if (c[0] > 0.0) { e2 += 1; } @@ -552,7 +780,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Search for the line ends: */ d1 = x - searchDiag2(x, y, -1, &d1_found); - sample(m_imageReader, x + 1, y, e); + sample_image_fn_(x + 1, y, e); if (e[0] > 0.0f) { d2 = searchDiag2(x, y, 1, &d2_found) - x; } @@ -568,11 +796,11 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int left = x - d1, top = y - d1; - sample(m_imageReader, left - 1, top, c); + sample_image_fn_(left - 1, top, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, left, top - 1, c); + sample_image_fn_(left, top - 1, c); if (c[0] > 0.0) { e1 += 1; } @@ -582,7 +810,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Fetch the crossing edges: */ int right = x + d2, bottom = y + d2; - sample(m_imageReader, right + 1, bottom, c); + sample_image_fn_(right + 1, bottom, c); if (c[1] > 0.0) { e2 += 2; } @@ -610,7 +838,7 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int } /* Search for the line ends: */ - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] > 0.0f) { d1 = x - searchDiag2(x - 1, y, -1, &found); } @@ -631,14 +859,14 @@ int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) float e[4]; while (x > end) { - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f) { /* Is the edge not activated? */ break; } if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return x; } - sample(m_imageReader, x, y - 1, e); + sample_image_fn_(x, y - 1, e); if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return x; } @@ -655,12 +883,12 @@ int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) while (x < end) { x++; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[1] == 0.0f || /* Is the edge not activated? */ e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } - sample(m_imageReader, x, y - 1, e); + sample_image_fn_(x, y - 1, e); if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } @@ -675,14 +903,14 @@ int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) float e[4]; while (y > end) { - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[0] == 0.0f) { /* Is the edge not activated? */ break; } if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return y; } - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ return y; } @@ -699,12 +927,12 @@ int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) while (y < end) { y++; - sample(m_imageReader, x, y, e); + sample_image_fn_(x, y, e); if (e[0] == 0.0f || /* Is the edge not activated? */ e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } - sample(m_imageReader, x - 1, y, e); + sample_image_fn_(x - 1, y, e); if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ break; } @@ -728,16 +956,16 @@ void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( /* Near the left corner */ if (d1 <= d2) { - sample(m_imageReader, left, y + 1, e); + sample_image_fn_(left, y + 1, e); factor[0] -= rounding * e[0]; - sample(m_imageReader, left, y - 2, e); + sample_image_fn_(left, y - 2, e); factor[1] -= rounding * e[0]; } /* Near the right corner */ if (d1 >= d2) { - sample(m_imageReader, right + 1, y + 1, e); + sample_image_fn_(right + 1, y + 1, e); factor[0] -= rounding * e[0]; - sample(m_imageReader, right + 1, y - 2, e); + sample_image_fn_(right + 1, y - 2, e); factor[1] -= rounding * e[0]; } @@ -757,16 +985,16 @@ void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( /* Near the top corner */ if (d1 <= d2) { - sample(m_imageReader, x + 1, top, e); + sample_image_fn_(x + 1, top, e); factor[0] -= rounding * e[1]; - sample(m_imageReader, x - 2, top, e); + sample_image_fn_(x - 2, top, e); factor[1] -= rounding * e[1]; } /* Near the bottom corner */ if (d1 >= d2) { - sample(m_imageReader, x + 1, bottom + 1, e); + sample_image_fn_(x + 1, bottom + 1, e); factor[0] -= rounding * e[1]; - sample(m_imageReader, x - 2, bottom + 1, e); + sample_image_fn_(x - 2, bottom + 1, e); factor[1] -= rounding * e[1]; } @@ -847,6 +1075,59 @@ void SMAANeighborhoodBlendingOperation::executePixel(float output[4], madd_v4_v4fl(output, color2, weight2); } +void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &out_area, + Span inputs) +{ + MemoryBuffer *image1 = inputs[0]; + MemoryBuffer *image2 = inputs[1]; + for (BuffersIterator it = output->iterate_with({}, out_area); !it.is_end(); ++it) { + const float x = it.x; + const float y = it.y; + float w[4]; + + /* Fetch the blending weights for current pixel: */ + image2->read_elem_checked(x, y, w); + const float left = w[2], top = w[0]; + image2->read_elem_checked(x + 1, y, w); + const float right = w[3]; + image2->read_elem_checked(x, y + 1, w); + const float bottom = w[1]; + + /* Is there any blending weight with a value greater than 0.0? */ + if (right + bottom + left + top < 1e-5f) { + image1->read_elem_checked(x, y, it.out); + continue; + } + + /* Calculate the blending offsets: */ + void (*sample_fn)(MemoryBuffer * reader, int x, int y, float xoffset, float color[4]); + float offset1, offset2, weight1, weight2, color1[4], color2[4]; + + if (fmaxf(right, left) > fmaxf(bottom, top)) { /* `max(horizontal) > max(vertical)` */ + sample_fn = sample_bilinear_horizontal; + offset1 = right; + offset2 = -left; + weight1 = right / (right + left); + weight2 = left / (right + left); + } + else { + sample_fn = sample_bilinear_vertical; + offset1 = bottom; + offset2 = -top; + weight1 = bottom / (bottom + top); + weight2 = top / (bottom + top); + } + + /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ + sample_fn(image1, x, y, offset1, color1); + sample_fn(image1, x, y, offset2, color2); + + mul_v4_v4fl(it.out, color1, weight1); + madd_v4_v4fl(it.out, color2, weight2); + } +} + void SMAANeighborhoodBlendingOperation::deinitExecution() { this->m_image1Reader = nullptr; @@ -866,4 +1147,12 @@ bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } +void SMAANeighborhoodBlendingOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area = output_area; + expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h index 781762202b4..91b9299ee43 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.h +++ b/source/blender/compositor/operations/COM_SMAAOperation.h @@ -20,14 +20,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { /*-----------------------------------------------------------------------------*/ /* Edge Detection (First Pass) */ -class SMAAEdgeDetectionOperation : public NodeOperation { +class SMAAEdgeDetectionOperation : public MultiThreadedOperation { protected: SocketReader *m_imageReader; SocketReader *m_valueReader; @@ -60,15 +60,20 @@ class SMAAEdgeDetectionOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; /*-----------------------------------------------------------------------------*/ /* Blending Weight Calculation (Second Pass) */ -class SMAABlendingWeightCalculationOperation : public NodeOperation { +class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: SocketReader *m_imageReader; - + std::function sample_image_fn_; int m_corner_rounding; public: @@ -96,6 +101,14 @@ class SMAABlendingWeightCalculationOperation : public NodeOperation { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + private: /* Diagonal Search Functions */ int searchDiag1(int x, int y, int dir, bool *found); @@ -117,7 +130,7 @@ class SMAABlendingWeightCalculationOperation : public NodeOperation { /*-----------------------------------------------------------------------------*/ /* Neighborhood Blending (Third Pass) */ -class SMAANeighborhoodBlendingOperation : public NodeOperation { +class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { private: SocketReader *m_image1Reader; SocketReader *m_image2Reader; @@ -144,6 +157,11 @@ class SMAANeighborhoodBlendingOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index df65044afc1..5405e6d424a 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -57,6 +57,7 @@ VectorBlurOperation::VectorBlurOperation() this->m_inputSpeedProgram = nullptr; this->m_inputZProgram = nullptr; flags.complex = true; + flags.is_fullframe_operation = true; } void VectorBlurOperation::initExecution() { @@ -121,6 +122,51 @@ bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, return false; } +void VectorBlurOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmin = 0; + r_input_area.xmax = this->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = this->getHeight(); +} + +void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ + if (!m_cachedInstance) { + MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; + const bool is_image_inflated = image->is_a_single_elem(); + image = is_image_inflated ? image->inflate() : image; + + /* Must be a copy because it's modified in #generateVectorBlur. */ + MemoryBuffer *speed = inputs[SPEED_INPUT_INDEX]; + speed = speed->is_a_single_elem() ? speed->inflate() : new MemoryBuffer(*speed); + + MemoryBuffer *z = inputs[Z_INPUT_INDEX]; + const bool is_z_inflated = z->is_a_single_elem(); + z = is_z_inflated ? z->inflate() : z; + + m_cachedInstance = (float *)MEM_dupallocN(image->getBuffer()); + this->generateVectorBlur(m_cachedInstance, image, speed, z); + + if (is_image_inflated) { + delete image; + } + delete speed; + if (is_z_inflated) { + delete z; + } + } + + const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); + MemoryBuffer buf(m_cachedInstance, num_channels, this->getWidth(), this->getHeight()); + output->copy_from(&buf, area); +} + void VectorBlurOperation::generateVectorBlur(float *data, MemoryBuffer *inputImage, MemoryBuffer *inputSpeed, diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index dfcf1fb16f7..c30c150db3c 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -26,6 +26,10 @@ namespace blender::compositor { class VectorBlurOperation : public NodeOperation, public QualityStepHelper { private: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int Z_INPUT_INDEX = 1; + static constexpr int SPEED_INPUT_INDEX = 2; + /** * \brief Cached reference to the inputProgram */ @@ -68,6 +72,13 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { ReadBufferOperation *readOperation, rcti *output) override; + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) override; + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + protected: void generateVectorBlur(float *data, MemoryBuffer *inputImage, -- cgit v1.2.3 From d84c79a218e63d0d752d918e2c1cbcc2f90fd35e Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 4 Sep 2021 15:23:47 +0200 Subject: Compositor: Full frame vector nodes Adds full frame implementation to Map Range, Map Value, Normal and Normalize nodes. The other nodes in "Vector" sub-menu are submitted separately. Part of T88150. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12233 --- .../operations/COM_DotproductOperation.cc | 12 +++++ .../operations/COM_DotproductOperation.h | 8 ++- .../compositor/operations/COM_MapRangeOperation.cc | 40 +++++++++++++++ .../compositor/operations/COM_MapRangeOperation.h | 8 ++- .../compositor/operations/COM_MapValueOperation.cc | 24 +++++++++ .../compositor/operations/COM_MapValueOperation.h | 8 ++- .../operations/COM_NormalizeOperation.cc | 58 ++++++++++++++++++++++ .../compositor/operations/COM_NormalizeOperation.h | 12 ++++- 8 files changed, 162 insertions(+), 8 deletions(-) diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 07075ae1d9d..875b161e208 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -28,6 +28,7 @@ DotproductOperation::DotproductOperation() this->setResolutionInputSocketIndex(0); this->m_input1Operation = nullptr; this->m_input2Operation = nullptr; + flags.can_be_constant = true; } void DotproductOperation::initExecution() { @@ -55,4 +56,15 @@ void DotproductOperation::executePixelSampled(float output[4], output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } +void DotproductOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *input1 = it.in(0); + const float *input2 = it.in(1); + *it.out = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h index 728033bcf32..c3f39d43fff 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.h +++ b/source/blender/compositor/operations/COM_DotproductOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class DotproductOperation : public NodeOperation { +class DotproductOperation : public MultiThreadedOperation { private: SocketReader *m_input1Operation; SocketReader *m_input2Operation; @@ -33,6 +33,10 @@ class DotproductOperation : public NodeOperation { void initExecution() override; void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index ada3cd6f159..82fb033bf24 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -30,6 +30,7 @@ MapRangeOperation::MapRangeOperation() this->addOutputSocket(DataType::Value); this->m_inputOperation = nullptr; this->m_useClamp = false; + flags.can_be_constant = true; } void MapRangeOperation::initExecution() @@ -104,4 +105,43 @@ void MapRangeOperation::deinitExecution() this->m_destMaxOperation = nullptr; } +void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float source_min = *it.in(1); + const float source_max = *it.in(2); + if (fabsf(source_max - source_min) < 1e-6f) { + it.out[0] = 0.0f; + continue; + } + + float value = *it.in(0); + const float dest_min = *it.in(3); + const float dest_max = *it.in(4); + if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) { + value = (value - source_min) / (source_max - source_min); + value = dest_min + value * (dest_max - dest_min); + } + else if (value > BLENDER_ZMAX) { + value = dest_max; + } + else { + value = dest_min; + } + + if (m_useClamp) { + if (dest_max > dest_min) { + CLAMP(value, dest_min, dest_max); + } + else { + CLAMP(value, dest_max, dest_min); + } + } + + it.out[0] = value; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index a544c59887e..a01be14d528 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MapRangeOperation : public NodeOperation { +class MapRangeOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -68,6 +68,10 @@ class MapRangeOperation : public NodeOperation { { this->m_useClamp = value; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 03fa80d220d..94fecc3f49e 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -25,6 +25,7 @@ MapValueOperation::MapValueOperation() this->addInputSocket(DataType::Value); this->addOutputSocket(DataType::Value); this->m_inputOperation = nullptr; + flags.can_be_constant = true; } void MapValueOperation::initExecution() @@ -60,4 +61,27 @@ void MapValueOperation::deinitExecution() this->m_inputOperation = nullptr; } +void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float input = *it.in(0); + TexMapping *texmap = this->m_settings; + float value = (input + texmap->loc[0]) * texmap->size[0]; + if (texmap->flag & TEXMAP_CLIP_MIN) { + if (value < texmap->min[0]) { + value = texmap->min[0]; + } + } + if (texmap->flag & TEXMAP_CLIP_MAX) { + if (value > texmap->max[0]) { + value = texmap->max[0]; + } + } + + it.out[0] = value; + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index eb7714714e9..a595eac3155 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * this program converts an input color to an output value. * it assumes we are in sRGB color space. */ -class MapValueOperation : public NodeOperation { +class MapValueOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -63,6 +63,10 @@ class MapValueOperation : public NodeOperation { { this->m_settings = settings; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index f93afcaab95..c3e72d2575f 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -27,6 +27,7 @@ NormalizeOperation::NormalizeOperation() this->m_imageReader = nullptr; this->m_cachedInstance = nullptr; this->flags.complex = true; + flags.can_be_constant = true; } void NormalizeOperation::initExecution() { @@ -56,6 +57,7 @@ void NormalizeOperation::deinitExecution() { this->m_imageReader = nullptr; delete this->m_cachedInstance; + m_cachedInstance = nullptr; NodeOperation::deinitMutex(); } @@ -127,4 +129,60 @@ void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) /* pass */ } +void NormalizeOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + NodeOperation *input = get_input_operation(0); + r_input_area.xmin = 0; + r_input_area.xmax = input->getWidth(); + r_input_area.ymin = 0; + r_input_area.ymax = input->getHeight(); +} + +void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span inputs) +{ + if (m_cachedInstance == nullptr) { + MemoryBuffer *input = inputs[0]; + + /* Using generic two floats struct to store `x: min`, `y: multiply`. */ + NodeTwoFloats *minmult = new NodeTwoFloats(); + + float minv = 1.0f + BLENDER_ZMAX; + float maxv = -1.0f - BLENDER_ZMAX; + for (const float *elem : input->as_range()) { + const float value = *elem; + if ((value > maxv) && (value <= BLENDER_ZMAX)) { + maxv = value; + } + if ((value < minv) && (value >= -BLENDER_ZMAX)) { + minv = value; + } + } + + minmult->x = minv; + /* The case of a flat buffer would cause a divide by 0. */ + minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); + + m_cachedInstance = minmult; + } +} + +void NormalizeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + NodeTwoFloats *minmult = m_cachedInstance; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float input_value = *it.in(0); + + *it.out = (input_value - minmult->x) * minmult->y; + + /* Clamp infinities. */ + CLAMP(*it.out, 0.0f, 1.0f); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index c89ba372189..7af2aad8a88 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" namespace blender::compositor { @@ -27,7 +27,7 @@ namespace blender::compositor { * \brief base class of normalize, implementing the simple normalize * \ingroup operation */ -class NormalizeOperation : public NodeOperation { +class NormalizeOperation : public MultiThreadedOperation { protected: /** * \brief Cached reference to the reader @@ -64,6 +64,14 @@ class NormalizeOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From b225a7c4705104245c2267101adec2f2ee2fe20a Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 4 Sep 2021 16:59:02 +0200 Subject: Compositor: Merge equal operations Some operations can take a lot of time to execute and any duplication should be avoided. This patch implements a compile step that detects operations with the same type, inputs and parameters that produce the same result and merge them. Now operations can generate a hash that represents their output result. They only need to implement `hash_output_params` and hash any parameter that affects the output result. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12341 --- source/blender/compositor/CMakeLists.txt | 1 + source/blender/compositor/intern/COM_Debug.cc | 3 +- .../blender/compositor/intern/COM_NodeOperation.cc | 43 ++++++ .../blender/compositor/intern/COM_NodeOperation.h | 70 +++++++++ .../compositor/intern/COM_NodeOperationBuilder.cc | 74 +++++++-- .../compositor/intern/COM_NodeOperationBuilder.h | 5 +- .../compositor/operations/COM_ConvertOperation.cc | 16 ++ .../compositor/operations/COM_ConvertOperation.h | 3 + .../compositor/tests/COM_NodeOperation_test.cc | 169 +++++++++++++++++++++ 9 files changed, 373 insertions(+), 11 deletions(-) create mode 100644 source/blender/compositor/tests/COM_NodeOperation_test.cc diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 8ddcf11602a..4f78d82f486 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -647,6 +647,7 @@ if(WITH_GTESTS) tests/COM_BufferArea_test.cc tests/COM_BufferRange_test.cc tests/COM_BuffersIterator_test.cc + tests/COM_NodeOperation_test.cc ) set(TEST_INC ) diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index a0333cf96cf..007085ee528 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -425,7 +425,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma } const bool has_execution_groups = system->getContext().get_execution_model() == - eExecutionModel::Tiled; + eExecutionModel::Tiled && + system->m_groups.size() > 0; len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups); len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n"); diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 1b87cdf72fb..a87485fd51c 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -41,6 +41,49 @@ NodeOperation::NodeOperation() this->m_btree = nullptr; } +/** + * Generate a hash that identifies the operation result in the current execution. + * Requires `hash_output_params` to be implemented, otherwise `std::nullopt` is returned. + * If the operation parameters or its linked inputs change, the hash must be re-generated. + */ +std::optional NodeOperation::generate_hash() +{ + params_hash_ = get_default_hash_2(m_width, m_height); + + /* Hash subclasses params. */ + is_hash_output_params_implemented_ = true; + hash_output_params(); + if (!is_hash_output_params_implemented_) { + return std::nullopt; + } + + hash_param(getOutputSocket()->getDataType()); + NodeOperationHash hash; + hash.params_hash_ = params_hash_; + + hash.parents_hash_ = 0; + for (NodeOperationInput &socket : m_inputs) { + NodeOperation &input = socket.getLink()->getOperation(); + const bool is_constant = input.get_flags().is_constant_operation; + combine_hashes(hash.parents_hash_, get_default_hash(is_constant)); + if (is_constant) { + const float *elem = ((ConstantOperation *)&input)->get_constant_elem(); + const int num_channels = COM_data_type_num_channels(socket.getDataType()); + for (const int i : IndexRange(num_channels)) { + combine_hashes(hash.parents_hash_, get_default_hash(elem[i])); + } + } + else { + combine_hashes(hash.parents_hash_, get_default_hash(input.get_id())); + } + } + + hash.type_hash_ = typeid(*this).hash_code(); + hash.operation_ = this; + + return hash; +} + NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) { return &m_outputs[index]; diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index b402dc7f174..ef7cf319222 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -22,6 +22,8 @@ #include #include +#include "BLI_ghash.h" +#include "BLI_hash.hh" #include "BLI_math_color.h" #include "BLI_math_vector.h" #include "BLI_threads.h" @@ -269,6 +271,42 @@ struct NodeOperationFlags { } }; +/** Hash that identifies an operation output result in the current execution. */ +struct NodeOperationHash { + private: + NodeOperation *operation_; + size_t type_hash_; + size_t parents_hash_; + size_t params_hash_; + + friend class NodeOperation; + + public: + NodeOperation *get_operation() const + { + return operation_; + } + + bool operator==(const NodeOperationHash &other) const + { + return type_hash_ == other.type_hash_ && parents_hash_ == other.parents_hash_ && + params_hash_ == other.params_hash_; + } + + bool operator!=(const NodeOperationHash &other) const + { + return !(*this == other); + } + + bool operator<(const NodeOperationHash &other) const + { + return type_hash_ < other.type_hash_ || + (type_hash_ == other.type_hash_ && parents_hash_ < other.parents_hash_) || + (type_hash_ == other.type_hash_ && parents_hash_ == other.parents_hash_ && + params_hash_ < other.params_hash_); + } +}; + /** * \brief NodeOperation contains calculation logic * @@ -282,6 +320,9 @@ class NodeOperation { Vector m_inputs; Vector m_outputs; + size_t params_hash_; + bool is_hash_output_params_implemented_; + /** * \brief the index of the input socket that will be used to determine the resolution */ @@ -363,6 +404,8 @@ class NodeOperation { return flags; } + std::optional generate_hash(); + unsigned int getNumberOfInputSockets() const { return m_inputs.size(); @@ -624,6 +667,33 @@ class NodeOperation { protected: NodeOperation(); + /* Overridden by subclasses to allow merging equal operations on compiling. Implementations must + * hash any subclass parameter that affects the output result using `hash_params` methods. */ + virtual void hash_output_params() + { + is_hash_output_params_implemented_ = false; + } + + static void combine_hashes(size_t &combined, size_t other) + { + combined = BLI_ghashutil_combine_hash(combined, other); + } + + template void hash_param(T param) + { + combine_hashes(params_hash_, get_default_hash(param)); + } + + template void hash_params(T1 param1, T2 param2) + { + combine_hashes(params_hash_, get_default_hash_2(param1, param2)); + } + + template void hash_params(T1 param1, T2 param2, T3 param3) + { + combine_hashes(params_hash_, get_default_hash_3(param1, param2, param3)); + } + void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); void addOutputSocket(DataType datatype); diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 10a91bbcd3e..5e18b5396b1 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -101,16 +101,16 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) add_datatype_conversions(); if (m_context->get_execution_model() == eExecutionModel::FullFrame) { - /* Copy operations to system. Needed for graphviz. */ - system->set_operations(m_operations, {}); - - DebugInfo::graphviz(system, "compositor_prior_folding"); + save_graphviz("compositor_prior_folding"); ConstantFolder folder(*this); folder.fold_operations(); } determineResolutions(); + save_graphviz("compositor_prior_merging"); + merge_equal_operations(); + if (m_context->get_execution_model() == eExecutionModel::Tiled) { /* surround complex ops with read/write buffer */ add_complex_operation_buffers(); @@ -149,22 +149,28 @@ void NodeOperationBuilder::replace_operation_with_constant(NodeOperation *operat ConstantOperation *constant_operation) { BLI_assert(constant_operation->getNumberOfInputSockets() == 0); + unlink_inputs_and_relink_outputs(operation, constant_operation); + addOperation(constant_operation); +} + +void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, + NodeOperation *linked_op) +{ int i = 0; while (i < m_links.size()) { Link &link = m_links[i]; - if (&link.to()->getOperation() == operation) { + if (&link.to()->getOperation() == unlinked_op) { link.to()->setLink(nullptr); m_links.remove(i); continue; } - if (&link.from()->getOperation() == operation) { - link.to()->setLink(constant_operation->getOutputSocket()); - m_links[i] = Link(constant_operation->getOutputSocket(), link.to()); + if (&link.from()->getOperation() == unlinked_op) { + link.to()->setLink(linked_op->getOutputSocket()); + m_links[i] = Link(linked_op->getOutputSocket(), link.to()); } i++; } - addOperation(constant_operation); } void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, @@ -456,6 +462,48 @@ void NodeOperationBuilder::determineResolutions() } } +static Vector generate_hashes(Span operations) +{ + Vector hashes; + for (NodeOperation *op : operations) { + std::optional hash = op->generate_hash(); + if (hash) { + hashes.append(std::move(*hash)); + } + } + return hashes; +} + +/** Merge operations with same type, inputs and parameters that produce the same result. */ +void NodeOperationBuilder::merge_equal_operations() +{ + bool any_merged = true; + while (any_merged) { + /* Re-generate hashes with any change. */ + Vector hashes = generate_hashes(m_operations); + + /* Make hashes be consecutive when they are equal. */ + std::sort(hashes.begin(), hashes.end()); + + any_merged = false; + const NodeOperationHash *prev_hash = nullptr; + for (const NodeOperationHash &hash : hashes) { + if (prev_hash && *prev_hash == hash) { + merge_equal_operations(prev_hash->get_operation(), hash.get_operation()); + any_merged = true; + } + prev_hash = &hash; + } + } +} + +void NodeOperationBuilder::merge_equal_operations(NodeOperation *from, NodeOperation *into) +{ + unlink_inputs_and_relink_outputs(from, into); + m_operations.remove_first_occurrence_and_reorder(from); + delete from; +} + Vector NodeOperationBuilder::cache_output_links( NodeOperationOutput *output) const { @@ -728,6 +776,14 @@ void NodeOperationBuilder::group_operations() } } +void NodeOperationBuilder::save_graphviz(StringRefNull name) +{ + if (COM_EXPORT_GRAPHVIZ) { + exec_system_->set_operations(m_operations, m_groups); + DebugInfo::graphviz(exec_system_, name); + } +} + /** Create a graphviz representation of the NodeOperationBuilder. */ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) { diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index 1f76765c846..aca4d043d41 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -169,7 +169,10 @@ class NodeOperationBuilder { private: PreviewOperation *make_preview_operation() const; - + void unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, NodeOperation *linked_op); + void merge_equal_operations(); + void merge_equal_operations(NodeOperation *from, NodeOperation *into); + void save_graphviz(StringRefNull name = ""); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompilerImpl") #endif diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index d377903efea..9a3733dda5b 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -40,6 +40,10 @@ void ConvertBaseOperation::deinitExecution() this->m_inputOperation = nullptr; } +void ConvertBaseOperation::hash_output_params() +{ +} + void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) @@ -269,6 +273,12 @@ void ConvertRGBToYCCOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertRGBToYCCOperation::hash_output_params() +{ + ConvertBaseOperation::hash_output_params(); + hash_param(m_mode); +} + void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator &it) { for (; !it.is_end(); ++it) { @@ -327,6 +337,12 @@ void ConvertYCCToRGBOperation::executePixelSampled(float output[4], output[3] = inputColor[3]; } +void ConvertYCCToRGBOperation::hash_output_params() +{ + ConvertBaseOperation::hash_output_params(); + hash_param(m_mode); +} + void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) { for (; !it.is_end(); ++it) { diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 0334959ae7e..72864b3c5e2 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -37,6 +37,7 @@ class ConvertBaseOperation : public MultiThreadedOperation { Span inputs) final; protected: + virtual void hash_output_params() override; virtual void update_memory_buffer_partial(BuffersIterator &it) = 0; }; @@ -124,6 +125,7 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { void setMode(int mode); protected: + void hash_output_params() override; void update_memory_buffer_partial(BuffersIterator &it) override; }; @@ -141,6 +143,7 @@ class ConvertYCCToRGBOperation : public ConvertBaseOperation { void setMode(int mode); protected: + void hash_output_params() override; void update_memory_buffer_partial(BuffersIterator &it) override; }; diff --git a/source/blender/compositor/tests/COM_NodeOperation_test.cc b/source/blender/compositor/tests/COM_NodeOperation_test.cc new file mode 100644 index 00000000000..94e9fdeedb1 --- /dev/null +++ b/source/blender/compositor/tests/COM_NodeOperation_test.cc @@ -0,0 +1,169 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "testing/testing.h" + +#include "COM_ConstantOperation.h" + +namespace blender::compositor::tests { + +class NonHashedOperation : public NodeOperation { + public: + NonHashedOperation(int id) + { + set_id(id); + addOutputSocket(DataType::Value); + setWidth(2); + setHeight(3); + } +}; + +class NonHashedConstantOperation : public ConstantOperation { + float constant_; + + public: + NonHashedConstantOperation(int id) + { + set_id(id); + addOutputSocket(DataType::Value); + setWidth(2); + setHeight(3); + constant_ = 1.0f; + } + + const float *get_constant_elem() override + { + return &constant_; + } + + void set_constant(float value) + { + constant_ = value; + } +}; + +class HashedOperation : public NodeOperation { + private: + int param1; + float param2; + + public: + HashedOperation(NodeOperation &input, int width, int height) + { + addInputSocket(DataType::Value); + addOutputSocket(DataType::Color); + setWidth(width); + setHeight(height); + param1 = 2; + param2 = 7.0f; + + getInputSocket(0)->setLink(input.getOutputSocket()); + } + + void set_param1(int value) + { + param1 = value; + } + + void hash_output_params() override + { + hash_params(param1, param2); + } +}; + +static void test_non_equal_hashes_compare(NodeOperationHash &h1, + NodeOperationHash &h2, + NodeOperationHash &h3) +{ + if (h1 < h2) { + if (h3 < h1) { + EXPECT_TRUE(h3 < h2); + } + else if (h3 < h2) { + EXPECT_TRUE(h1 < h3); + } + else { + EXPECT_TRUE(h1 < h3); + EXPECT_TRUE(h2 < h3); + } + } + else { + EXPECT_TRUE(h2 < h1); + } +} + +TEST(NodeOperation, generate_hash) +{ + /* Constant input. */ + { + NonHashedConstantOperation input_op1(1); + input_op1.set_constant(1.0f); + EXPECT_EQ(input_op1.generate_hash(), std::nullopt); + + HashedOperation op1(input_op1, 6, 4); + std::optional hash1_opt = op1.generate_hash(); + EXPECT_NE(hash1_opt, std::nullopt); + NodeOperationHash hash1 = *hash1_opt; + + NonHashedConstantOperation input_op2(1); + input_op2.set_constant(1.0f); + HashedOperation op2(input_op2, 6, 4); + NodeOperationHash hash2 = *op2.generate_hash(); + EXPECT_EQ(hash1, hash2); + + input_op2.set_constant(3.0f); + hash2 = *op2.generate_hash(); + EXPECT_NE(hash1, hash2); + } + + /* Non constant input. */ + { + NonHashedOperation input_op(1); + EXPECT_EQ(input_op.generate_hash(), std::nullopt); + + HashedOperation op1(input_op, 6, 4); + HashedOperation op2(input_op, 6, 4); + NodeOperationHash hash1 = *op1.generate_hash(); + NodeOperationHash hash2 = *op2.generate_hash(); + EXPECT_EQ(hash1, hash2); + op1.set_param1(-1); + hash1 = *op1.generate_hash(); + EXPECT_NE(hash1, hash2); + + HashedOperation op3(input_op, 11, 14); + NodeOperationHash hash3 = *op3.generate_hash(); + EXPECT_NE(hash2, hash3); + EXPECT_NE(hash1, hash3); + + test_non_equal_hashes_compare(hash1, hash2, hash3); + test_non_equal_hashes_compare(hash3, hash2, hash1); + test_non_equal_hashes_compare(hash2, hash3, hash1); + test_non_equal_hashes_compare(hash3, hash1, hash2); + + NonHashedOperation input_op2(2); + HashedOperation op4(input_op2, 11, 14); + NodeOperationHash hash4 = *op4.generate_hash(); + EXPECT_NE(hash3, hash4); + + input_op2.set_id(1); + hash4 = *op4.generate_hash(); + EXPECT_EQ(hash3, hash4); + } +} + +} // namespace blender::compositor::tests -- cgit v1.2.3 From 079bd115563640b81cccba645d15b6b55efd3b8e Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sat, 4 Sep 2021 18:28:31 +0200 Subject: Fix T91143: Gpencil Set Vertex Color not using Linear The color was not converted to Linear from Brush color. --- source/blender/editors/gpencil/gpencil_vertex_ops.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index 402bccce2f7..5c3a7cf9e6f 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -588,6 +588,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) changed = true; copy_v3_v3(gps->vert_color_fill, brush->rgb); gps->vert_color_fill[3] = factor; + srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill); } /* Stroke points. */ @@ -596,10 +597,13 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op) int i; bGPDspoint *pt; + float color[4]; + copy_v3_v3(color, brush->rgb); + color[3] = factor; + srgb_to_linearrgb_v4(color, color); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((!any_selected) || (pt->flag & GP_SPOINT_SELECT)) { - copy_v3_v3(pt->vert_color, brush->rgb); - pt->vert_color[3] = factor; + copy_v3_v3(pt->vert_color, color); } } } -- cgit v1.2.3 From b7718bbdf55c1a217a5224093ebb59b9a39135f6 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sun, 5 Sep 2021 14:25:14 +0200 Subject: Cleanup: improve code clarity Addresses D12341 review. --- source/blender/compositor/intern/COM_NodeOperationBuilder.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index 5e18b5396b1..b2cd76be2c3 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -477,15 +477,15 @@ static Vector generate_hashes(Span operation /** Merge operations with same type, inputs and parameters that produce the same result. */ void NodeOperationBuilder::merge_equal_operations() { - bool any_merged = true; - while (any_merged) { + bool check_for_next_merge = true; + while (check_for_next_merge) { /* Re-generate hashes with any change. */ Vector hashes = generate_hashes(m_operations); /* Make hashes be consecutive when they are equal. */ std::sort(hashes.begin(), hashes.end()); - any_merged = false; + bool any_merged = false; const NodeOperationHash *prev_hash = nullptr; for (const NodeOperationHash &hash : hashes) { if (prev_hash && *prev_hash == hash) { @@ -494,6 +494,8 @@ void NodeOperationBuilder::merge_equal_operations() } prev_hash = &hash; } + + check_for_next_merge = any_merged; } } -- cgit v1.2.3 From d10ea97053c5bca605d94ddb09dd2c5934fcccfb Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 5 Sep 2021 15:22:30 -0400 Subject: Compositor: New Posterize Node The posterize node limits the number of colors per channel. This is useful to generate masks or to generate stylized images Both the tiled and full-frame implementation are included in this patch {F10314012} Reviewed By: manzanilla, jbakker Differential Revision: https://developer.blender.org/D12304 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/compositor/CMakeLists.txt | 4 ++ source/blender/compositor/intern/COM_Converter.cc | 4 ++ .../blender/compositor/nodes/COM_PosterizeNode.cc | 41 +++++++++++ .../blender/compositor/nodes/COM_PosterizeNode.h | 36 ++++++++++ .../operations/COM_PosterizeOperation.cc | 82 ++++++++++++++++++++++ .../compositor/operations/COM_PosterizeOperation.h | 56 +++++++++++++++ source/blender/makesrna/RNA_access.h | 1 + source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_composite.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../composite/nodes/node_composite_posterize.c | 46 ++++++++++++ 14 files changed, 276 insertions(+) create mode 100644 source/blender/compositor/nodes/COM_PosterizeNode.cc create mode 100644 source/blender/compositor/nodes/COM_PosterizeNode.h create mode 100644 source/blender/compositor/operations/COM_PosterizeOperation.cc create mode 100644 source/blender/compositor/operations/COM_PosterizeOperation.h create mode 100644 source/blender/nodes/composite/nodes/node_composite_posterize.c diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index d78023b4e0e..668ea4bfae5 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -333,6 +333,7 @@ compositor_node_categories = [ NodeItem("CompositorNodeGamma"), NodeItem("CompositorNodeExposure"), NodeItem("CompositorNodeColorCorrection"), + NodeItem("CompositorNodePosterize"), NodeItem("CompositorNodeTonemap"), NodeItem("CompositorNodeZcombine"), ]), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 5e0526ab262..b41fbae6075 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1255,6 +1255,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_NODE_DENOISE 324 #define CMP_NODE_EXPOSURE 325 #define CMP_NODE_CRYPTOMATTE 326 +#define CMP_NODE_POSTERIZE 327 /* channel toggles */ #define CMP_CHAN_RGB 1 diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 48d747e2bf3..be4fd7aac4f 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4919,6 +4919,7 @@ static void registerCompositNodes() register_node_type_cmp_inpaint(); register_node_type_cmp_despeckle(); register_node_type_cmp_defocus(); + register_node_type_cmp_posterize(); register_node_type_cmp_sunbeams(); register_node_type_cmp_denoise(); register_node_type_cmp_antialiasing(); diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 4f78d82f486..10e385e0187 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -317,6 +317,8 @@ set(SRC nodes/COM_FilterNode.h nodes/COM_InpaintNode.cc nodes/COM_InpaintNode.h + nodes/COM_PosterizeNode.cc + nodes/COM_PosterizeNode.h operations/COM_BlurBaseOperation.cc operations/COM_BlurBaseOperation.h @@ -346,6 +348,8 @@ set(SRC operations/COM_MovieClipAttributeOperation.h operations/COM_MovieDistortionOperation.cc operations/COM_MovieDistortionOperation.h + operations/COM_PosterizeOperation.cc + operations/COM_PosterizeOperation.h operations/COM_SMAAOperation.cc operations/COM_SMAAOperation.h operations/COM_VariableSizeBokehBlurOperation.cc diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 1983eb190e2..6049f14ecdf 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -41,6 +41,7 @@ #include "COM_ColorExposureNode.h" #include "COM_ColorMatteNode.h" #include "COM_ColorNode.h" +#include "COM_PosterizeNode.h" #include "COM_ColorRampNode.h" #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" @@ -424,6 +425,9 @@ Node *COM_convert_bnode(bNode *b_node) case CMP_NODE_ANTIALIASING: node = new AntiAliasingNode(b_node); break; + case CMP_NODE_POSTERIZE: + node = new PosterizeNode(b_node); + break; } return node; } diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc new file mode 100644 index 00000000000..8de42cc4ac4 --- /dev/null +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -0,0 +1,41 @@ +/* + * 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. + * + * Copyright 2020, Blender Foundation. + */ + +#include "COM_PosterizeNode.h" +#include "COM_PosterizeOperation.h" +#include "COM_ExecutionSystem.h" + +namespace blender::compositor { + +PosterizeNode::PosterizeNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +void PosterizeNode::convertToOperations(NodeConverter &converter, + const CompositorContext & /*context*/) const +{ + PosterizeOperation *operation = new PosterizeOperation(); + converter.addOperation(operation); + + converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.h b/source/blender/compositor/nodes/COM_PosterizeNode.h new file mode 100644 index 00000000000..bb9bef2bdd0 --- /dev/null +++ b/source/blender/compositor/nodes/COM_PosterizeNode.h @@ -0,0 +1,36 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_Node.h" + +namespace blender::compositor { + +/** + * \brief PosterizeNode + * \ingroup Node + */ +class PosterizeNode : public Node { + public: + PosterizeNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, + const CompositorContext &context) const override; +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc new file mode 100644 index 00000000000..db5860f48f8 --- /dev/null +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -0,0 +1,82 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#include "COM_PosterizeOperation.h" + +namespace blender::compositor { + +PosterizeOperation::PosterizeOperation() +{ + this->addInputSocket(DataType::Color); + this->addInputSocket(DataType::Value); + this->addOutputSocket(DataType::Color); + this->m_inputProgram = nullptr; + this->m_inputStepsProgram = nullptr; + flags.can_be_constant = true; +} + +void PosterizeOperation::initExecution() +{ + this->m_inputProgram = this->getInputSocketReader(0); + this->m_inputStepsProgram = this->getInputSocketReader(1); +} + +void PosterizeOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float inputValue[4]; + float inputSteps[4]; + + this->m_inputProgram->readSampled(inputValue, x, y, sampler); + this->m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); + CLAMP(inputSteps[0], 2.0f, 1024.0f); + const float steps_inv = 1.0f / inputSteps[0]; + + output[0] = floor(inputValue[0] / steps_inv) * steps_inv; + output[1] = floor(inputValue[1] / steps_inv) * steps_inv; + output[2] = floor(inputValue[2] / steps_inv) * steps_inv; + output[3] = inputValue[3]; +} + +void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_value = it.in(0); + const float *in_steps = it.in(1); + float steps = in_steps[0]; + CLAMP(steps, 2.0f, 1024.0f); + const float steps_inv = 1.0f / steps; + + it.out[0] = floor(in_value[0] / steps_inv) * steps_inv; + it.out[1] = floor(in_value[1] / steps_inv) * steps_inv; + it.out[2] = floor(in_value[2] / steps_inv) * steps_inv; + it.out[3] = in_value[3]; + } +} + +void PosterizeOperation::deinitExecution() +{ + this->m_inputProgram = nullptr; + this->m_inputStepsProgram = nullptr; +} + +} // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.h b/source/blender/compositor/operations/COM_PosterizeOperation.h new file mode 100644 index 00000000000..c625cbb83c6 --- /dev/null +++ b/source/blender/compositor/operations/COM_PosterizeOperation.h @@ -0,0 +1,56 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "COM_MultiThreadedOperation.h" + +namespace blender::compositor { + +class PosterizeOperation : public MultiThreadedOperation { + private: + /** + * Cached reference to the inputProgram + */ + SocketReader *m_inputProgram; + SocketReader *m_inputStepsProgram; + + public: + PosterizeOperation(); + + /** + * The inner loop of this operation. + */ + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + /** + * Initialize the execution + */ + void initExecution() override; + + /** + * Deinitialize the execution + */ + void deinitExecution() override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; +}; + +} // namespace blender::compositor diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 75057c1a071..c2ea901f4a1 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -177,6 +177,7 @@ extern StructRNA RNA_CompositorNodeMixRGB; extern StructRNA RNA_CompositorNodeNormal; extern StructRNA RNA_CompositorNodeNormalize; extern StructRNA RNA_CompositorNodeOutputFile; +extern StructRNA RNA_CompositorNodePosterize; extern StructRNA RNA_CompositorNodePremulKey; extern StructRNA RNA_CompositorNodeRGB; extern StructRNA RNA_CompositorNodeRGBToBW; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 29a1de381b5..1e7ac4b92f6 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -104,6 +104,7 @@ set(SRC composite/nodes/node_composite_outputFile.c composite/nodes/node_composite_pixelate.c composite/nodes/node_composite_planetrackdeform.c + composite/nodes/node_composite_posterize.c composite/nodes/node_composite_premulkey.c composite/nodes/node_composite_rgb.c composite/nodes/node_composite_rotate.c diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 258e4c961c9..2cbbd31c97a 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -80,6 +80,7 @@ void register_node_type_cmp_despeckle(void); void register_node_type_cmp_defocus(void); void register_node_type_cmp_denoise(void); void register_node_type_cmp_antialiasing(void); +void register_node_type_cmp_posterize(void); void register_node_type_cmp_valtorgb(void); void register_node_type_cmp_rgbtobw(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 8028350418a..2e9f186de02 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -225,6 +225,7 @@ DefNode(CompositorNode, CMP_NODE_CRYPTOMATTE_LEGACY, def_cmp_cryptomatte_legacy, DefNode(CompositorNode, CMP_NODE_DENOISE, def_cmp_denoise, "DENOISE", Denoise, "Denoise", "" ) DefNode(CompositorNode, CMP_NODE_EXPOSURE, 0, "EXPOSURE", Exposure, "Exposure", "" ) DefNode(CompositorNode, CMP_NODE_ANTIALIASING, def_cmp_antialiasing, "ANTIALIASING", AntiAliasing, "Anti-Aliasing", "" ) +DefNode(CompositorNode, CMP_NODE_POSTERIZE, 0, "POSTERIZE", Posterize, "Posterize", "" ) DefNode(TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) DefNode(TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.c b/source/blender/nodes/composite/nodes/node_composite_posterize.c new file mode 100644 index 00000000000..5093e581cdc --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.c @@ -0,0 +1,46 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup cmpnodes + */ + +#include "node_composite_util.h" + +/* **************** Posterize ******************** */ + +static bNodeSocketTemplate cmp_node_posterize_in[] = { + {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + {SOCK_FLOAT, N_("Steps"), 8.0f, 8.0f, 8.0f, 8.0f, 2.0f, 1024.0f, PROP_NONE}, + {-1, ""}, +}; +static bNodeSocketTemplate cmp_node_posterize_out[] = { + {SOCK_RGBA, N_("Image")}, + {-1, ""}, +}; + +void register_node_type_cmp_posterize(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR, 0); + node_type_socket_templates(&ntype, cmp_node_posterize_in, cmp_node_posterize_out); + + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 4ddad5a7ee5d9c3245593c509714f8c1359f9342 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 5 Sep 2021 21:11:40 -0400 Subject: UI: Split Output Properties Dimensions panel I remember when we originally decided on the Dimensions panel, one of the reasons we combined time and image size properties in the same panel, was simply because the 2.49 and previous UIs used fixed-size panels, so we often put two categories of properties inside a panel, using two columns. Now that we no longer do this, we could clarify and simplify some panels by splitting them, such as the Output > Dimensions panel {F6753690} Reviewed By: brecht, pablovazquez Differential Revision: https://developer.blender.org/D4440 --- release/scripts/startup/bl_ui/properties_output.py | 60 +++++++++++++--------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_output.py b/release/scripts/startup/bl_ui/properties_output.py index 0c1a26ceec1..d96a53f6ab8 100644 --- a/release/scripts/startup/bl_ui/properties_output.py +++ b/release/scripts/startup/bl_ui/properties_output.py @@ -25,8 +25,8 @@ from bl_ui.utils import PresetPanel from bpy.app.translations import pgettext_tip as tip_ -class RENDER_PT_presets(PresetPanel, Panel): - bl_label = "Render Presets" +class RENDER_PT_format_presets(PresetPanel, Panel): + bl_label = "Format Presets" preset_subdir = "render" preset_operator = "script.execute_preset" preset_add_operator = "render.preset_add" @@ -56,21 +56,21 @@ class RenderOutputButtonsPanel: return (context.engine in cls.COMPAT_ENGINES) -class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): - bl_label = "Dimensions" +class RENDER_PT_format(RenderOutputButtonsPanel, Panel): + bl_label = "Format" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} _frame_rate_args_prev = None _preset_class = None def draw_header_preset(self, _context): - RENDER_PT_presets.draw_panel_header(self.layout) + RENDER_PT_format_presets.draw_panel_header(self.layout) @staticmethod def _draw_framerate_label(*args): # avoids re-creating text string each draw - if RENDER_PT_dimensions._frame_rate_args_prev == args: - return RENDER_PT_dimensions._frame_rate_ret + if RENDER_PT_format._frame_rate_args_prev == args: + return RENDER_PT_format._frame_rate_ret fps, fps_base, preset_label = args @@ -89,17 +89,17 @@ class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): fps_label_text = tip_("%.4g fps") % fps_rate show_framerate = (preset_label == "Custom") - RENDER_PT_dimensions._frame_rate_args_prev = args - RENDER_PT_dimensions._frame_rate_ret = args = (fps_label_text, show_framerate) + RENDER_PT_format._frame_rate_args_prev = args + RENDER_PT_format._frame_rate_ret = args = (fps_label_text, show_framerate) return args @staticmethod def draw_framerate(layout, rd): - if RENDER_PT_dimensions._preset_class is None: - RENDER_PT_dimensions._preset_class = bpy.types.RENDER_MT_framerate_presets + if RENDER_PT_format._preset_class is None: + RENDER_PT_format._preset_class = bpy.types.RENDER_MT_framerate_presets - args = rd.fps, rd.fps_base, RENDER_PT_dimensions._preset_class.bl_label - fps_label_text, show_framerate = RENDER_PT_dimensions._draw_framerate_label(*args) + args = rd.fps, rd.fps_base, RENDER_PT_format._preset_class.bl_label + fps_label_text, show_framerate = RENDER_PT_format._draw_framerate_label(*args) layout.menu("RENDER_MT_framerate_presets", text=fps_label_text) @@ -113,8 +113,7 @@ class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False # No animation. - scene = context.scene - rd = scene.render + rd = context.scene.render col = layout.column(align=True) col.prop(rd, "resolution_x", text="Resolution X") @@ -131,18 +130,30 @@ class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel): sub.active = rd.use_border sub.prop(rd, "use_crop_to_border") + col = layout.column(heading="Frame Rate") + self.draw_framerate(col, rd) + + +class RENDER_PT_frame_range(RenderOutputButtonsPanel, Panel): + bl_label = "Frame Range" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + scene = context.scene + col = layout.column(align=True) col.prop(scene, "frame_start", text="Frame Start") col.prop(scene, "frame_end", text="End") col.prop(scene, "frame_step", text="Step") - col = layout.column(heading="Frame Rate") - self.draw_framerate(col, rd) - -class RENDER_PT_frame_remapping(RenderOutputButtonsPanel, Panel): - bl_label = "Time Remapping" - bl_parent_id = "RENDER_PT_dimensions" +class RENDER_PT_time_stretching(RenderOutputButtonsPanel, Panel): + bl_label = "Time Stretching" + bl_parent_id = "RENDER_PT_frame_range" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -481,11 +492,12 @@ class RENDER_PT_stereoscopy(RenderOutputButtonsPanel, Panel): classes = ( - RENDER_PT_presets, + RENDER_PT_format_presets, RENDER_PT_ffmpeg_presets, RENDER_MT_framerate_presets, - RENDER_PT_dimensions, - RENDER_PT_frame_remapping, + RENDER_PT_format, + RENDER_PT_frame_range, + RENDER_PT_time_stretching, RENDER_PT_stereoscopy, RENDER_PT_output, RENDER_PT_output_views, -- cgit v1.2.3 From bf0ac711fde2e0439af117f73ab1b3a758969348 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sun, 5 Sep 2021 19:55:50 -0700 Subject: UI: Increase Size of Blend File Thumbnails Increase effective resolution of blend preview images from 128x128 to 256x256 for versions saved in the file system thumbnail cache. See D10491 for details and examples. Differential Revision: https://developer.blender.org/D10491 Reviewed by Campbell Barton --- source/blender/imbuf/IMB_thumbs.h | 1 + source/blender/imbuf/intern/thumbs.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index 9dd0cbe895f..e1a315a0bd2 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -52,6 +52,7 @@ typedef enum ThumbSource { #define THUMB_SIZE_MAX (100 * 1024 * 1024) #define PREVIEW_RENDER_DEFAULT_HEIGHT 128 +#define PREVIEW_RENDER_LARGE_HEIGHT 256 /* Note this can also be used as versioning system, * to force refreshing all thumbnails if e.g. we change some thumb generating code or so. diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index a09f06726a6..aa1da65253d 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -347,7 +347,7 @@ static ImBuf *thumb_create_ex(const char *file_path, tsize = PREVIEW_RENDER_DEFAULT_HEIGHT; break; case THB_LARGE: - tsize = PREVIEW_RENDER_DEFAULT_HEIGHT * 2; + tsize = PREVIEW_RENDER_LARGE_HEIGHT; break; case THB_FAIL: tsize = 1; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index f83511e76f0..8eaac47471a 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1573,8 +1573,8 @@ static ImBuf *blend_file_thumb(const bContext *C, NULL, OB_SOLID, scene->camera, - BLEN_THUMB_SIZE * 2, - BLEN_THUMB_SIZE * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, IB_rect, V3D_OFSDRAW_NONE, R_ALPHAPREMUL, @@ -1588,8 +1588,8 @@ static ImBuf *blend_file_thumb(const bContext *C, OB_SOLID, v3d, region, - BLEN_THUMB_SIZE * 2, - BLEN_THUMB_SIZE * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, + PREVIEW_RENDER_LARGE_HEIGHT * 2, IB_rect, R_ALPHAPREMUL, NULL, @@ -1610,8 +1610,14 @@ static ImBuf *blend_file_thumb(const bContext *C, if (ibuf) { /* dirty oversampling */ - IMB_scaleImBuf(ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); - thumb = BKE_main_thumbnail_from_imbuf(NULL, ibuf); + ImBuf *thumb_ibuf; + thumb_ibuf = IMB_dupImBuf(ibuf); + /* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */ + IMB_scaleImBuf(thumb_ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); + thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); + IMB_freeImBuf(thumb_ibuf); + /* Thumbnail saved to filesystem should be 256x256. */ + IMB_scaleImBuf(ibuf, PREVIEW_RENDER_LARGE_HEIGHT, PREVIEW_RENDER_LARGE_HEIGHT); } else { /* '*thumb_pt' needs to stay NULL to prevent a bad thumbnail from being handled */ -- cgit v1.2.3 From 91bca410c0e08c6d1cd227336df91faddab7a767 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 5 Sep 2021 23:25:36 -0400 Subject: Cleanup: clang-format --- source/blender/compositor/intern/COM_Converter.cc | 2 +- source/blender/compositor/nodes/COM_PosterizeNode.cc | 2 +- .../blender/draw/engines/overlay/shaders/grid_background_frag.glsl | 6 +++--- source/blender/editors/gpencil/gpencil_utils.c | 3 ++- source/blender/sequencer/intern/effects.c | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 6049f14ecdf..4b103c21c75 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -41,7 +41,6 @@ #include "COM_ColorExposureNode.h" #include "COM_ColorMatteNode.h" #include "COM_ColorNode.h" -#include "COM_PosterizeNode.h" #include "COM_ColorRampNode.h" #include "COM_ColorSpillNode.h" #include "COM_ColorToBWNode.h" @@ -91,6 +90,7 @@ #include "COM_OutputFileNode.h" #include "COM_PixelateNode.h" #include "COM_PlaneTrackDeformNode.h" +#include "COM_PosterizeNode.h" #include "COM_RenderLayersNode.h" #include "COM_RotateNode.h" #include "COM_ScaleNode.h" diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc index 8de42cc4ac4..9f5a69961a4 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.cc +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -17,8 +17,8 @@ */ #include "COM_PosterizeNode.h" -#include "COM_PosterizeOperation.h" #include "COM_ExecutionSystem.h" +#include "COM_PosterizeOperation.h" namespace blender::compositor { diff --git a/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl index fcc05414ea3..f09918da6dc 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_background_frag.glsl @@ -6,7 +6,7 @@ out vec4 fragColor; void main() { - fragColor = color; - float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; - fragColor.a = (scene_depth == 1.0) ? 1.0 : 0.0; + fragColor = color; + float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + fragColor.a = (scene_depth == 1.0) ? 1.0 : 0.0; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 72d10d840fa..bb05b93ad81 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -3284,7 +3284,8 @@ bGPDstroke *ED_gpencil_stroke_nearest_to_ends(bContext *C, gpencil_point_to_parent_space(pt, diff_mat, &pt_parent); gpencil_point_to_xy_fl(gsc, gps, &pt_parent, &pt2d_target_end[0], &pt2d_target_end[1]); - /* If the distance to the original stroke extremes is too big, the stroke must not be joined. */ + /* If the distance to the original stroke extremes is too big, the stroke must not be joined. + */ if ((len_squared_v2v2(ctrl1, pt2d_target_start) > radius_sqr) && (len_squared_v2v2(ctrl1, pt2d_target_end) > radius_sqr) && (len_squared_v2v2(ctrl2, pt2d_target_start) > radius_sqr) && diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 80314d34360..4448db013fe 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -3188,7 +3188,7 @@ float seq_speed_effect_target_frame_get(Scene *scene, case SEQ_SPEED_STRETCH: { /* Only right handle controls effect speed! */ const float target_content_length = seq_effect_speed_get_strip_content_length(source) - - source->startofs; + source->startofs; const float speed_effetct_length = seq_speed->enddisp - seq_speed->startdisp; const float ratio = frame_index / speed_effetct_length; target_frame = target_content_length * ratio; -- cgit v1.2.3 From 58632a7f3c0f1be6cc860c7cad9c41ba43e6454f Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sun, 5 Sep 2021 21:05:07 -0700 Subject: UI: Blend Preview Thumbnails Showing Workspace This adds an option to use a capture of the entire main window as the blend file preview thumbnail. See D10492 for details and examples. Differential Revision: https://developer.blender.org/D10492 Reviewed by Campbell Barton --- release/datafiles/userdef/userdef_default.c | 5 +- release/scripts/startup/bl_ui/space_userpref.py | 2 +- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/icons.cc | 2 +- .../blender/blenloader/intern/versioning_userdef.c | 8 +++ source/blender/makesdna/DNA_userdef_types.h | 12 +++- source/blender/makesrna/intern/rna_userdef.c | 16 +++-- source/blender/windowmanager/intern/wm_files.c | 69 +++++++++++++++++++--- 8 files changed, 96 insertions(+), 20 deletions(-) diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index d51a82c482b..29288dcd8fd 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -33,8 +33,8 @@ const UserDef U_default = { .versionfile = BLENDER_FILE_VERSION, .subversionfile = BLENDER_FILE_SUBVERSION, - .flag = (USER_AUTOSAVE | USER_TOOLTIPS | USER_SAVE_PREVIEWS | USER_RELPATHS | - USER_RELEASECONFIRM | USER_SCRIPT_AUTOEXEC_DISABLE | USER_NONEGFRAMES), + .flag = (USER_AUTOSAVE | USER_TOOLTIPS | USER_RELPATHS | USER_RELEASECONFIRM | + USER_SCRIPT_AUTOEXEC_DISABLE | USER_NONEGFRAMES), .dupflag = USER_DUP_MESH | USER_DUP_CURVE | USER_DUP_SURF | USER_DUP_FONT | USER_DUP_MBALL | USER_DUP_LAMP | USER_DUP_ARM | USER_DUP_ACT | USER_DUP_LIGHTPROBE | USER_DUP_GPENCIL, @@ -231,6 +231,7 @@ const UserDef U_default = { .collection_instance_empty_size = 1.0f, .statusbar_flag = STATUSBAR_SHOW_VERSION, + .file_preview_type = USER_FILE_PREVIEW_CAMERA, .runtime = { diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 708701c4804..1ac19d020d8 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1414,7 +1414,7 @@ class USERPREF_PT_saveload_blend(SaveLoadPanel, CenterAlignMixIn, Panel): col = layout.column(heading="Save") col.prop(view, "use_save_prompt") - col.prop(paths, "use_save_preview_images") + col.prop(paths, "file_preview_type") col = layout.column(heading="Default To") col.prop(paths, "use_relative_paths") diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 5dc4ebaa7a4..e2788020628 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 20 +#define BLENDER_FILE_SUBVERSION 21 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 5a4b2448a73..ac45e57f413 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -634,7 +634,7 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) PreviewImage prv_copy = *prv; /* don't write out large previews if not requested */ - if (!(U.flag & USER_SAVE_PREVIEWS)) { + if (U.file_preview_type == USER_FILE_PREVIEW_NONE) { prv_copy.w[1] = 0; prv_copy.h[1] = 0; prv_copy.rect[1] = nullptr; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 0042ff29dc2..19f6c1cbbf6 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -885,6 +885,14 @@ void blo_do_versions_userdef(UserDef *userdef) BKE_addon_ensure(&userdef->addons, "pose_library"); } + if (!USER_VERSION_ATLEAST(300, 21)) { + /* Deprecated userdef->flag USER_SAVE_PREVIEWS */ + userdef->file_preview_type = (userdef->flag & USER_FLAG_UNUSED_5) ? USER_FILE_PREVIEW_CAMERA : + USER_FILE_PREVIEW_NONE; + /* Clear for reuse. */ + userdef->flag &= ~USER_FLAG_UNUSED_5; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 27376432092..b519a6014f0 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -924,8 +924,9 @@ typedef struct UserDef { short sequencer_proxy_setup; /* eUserpref_SeqProxySetup */ float collection_instance_empty_size; - char _pad10[3]; + char _pad10[2]; + char file_preview_type; /* eUserpref_File_Preview_Type */ char statusbar_flag; /* eUserpref_StatusBar_Flag */ struct WalkNavigation walk_navigation; @@ -996,7 +997,7 @@ typedef enum eUserPref_Flag { USER_NONUMPAD = (1 << 13), USER_ADD_CURSORALIGNED = (1 << 14), USER_FILECOMPRESS = (1 << 15), - USER_SAVE_PREVIEWS = (1 << 16), + USER_FLAG_UNUSED_5 = (1 << 16), /* dirty */ USER_CUSTOM_RANGE = (1 << 17), USER_ADD_EDITMODE = (1 << 18), USER_ADD_VIEWALIGNED = (1 << 19), @@ -1010,6 +1011,13 @@ typedef enum eUserPref_Flag { USER_FLAG_UNUSED_27 = (1 << 27), /* dirty */ } eUserPref_Flag; +/** #UserDef.file_preview_type */ +typedef enum eUserpref_File_Preview_Type { + USER_FILE_PREVIEW_NONE = 0, + USER_FILE_PREVIEW_SCREENSHOT, + USER_FILE_PREVIEW_CAMERA, +} eUserpref_File_Preview_Type; + typedef enum eUserPref_PrefFlag { USER_PREF_FLAG_SAVE = (1 << 0), } eUserPref_PrefFlag; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 73811924c23..fe1b0757690 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6059,6 +6059,13 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem preview_type_items[] = { + {USER_FILE_PREVIEW_NONE, "NONE", 0, "None", "Do not create blend previews"}, + {USER_FILE_PREVIEW_SCREENSHOT, "SCREENSHOT", 0, "Screenshot", "Capture the entire window"}, + {USER_FILE_PREVIEW_CAMERA, "CAMERA", 0, "Camera View", "Workbench render of scene"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "PreferencesFilePaths", NULL); RNA_def_struct_sdna(srna, "UserDef"); RNA_def_struct_nested(brna, srna, "Preferences"); @@ -6214,12 +6221,9 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Recent Files", "Maximum number of recently opened files to remember"); - prop = RNA_def_property(srna, "use_save_preview_images", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_SAVE_PREVIEWS); - RNA_def_property_ui_text(prop, - "Save Preview Images", - "Enables automatic saving of preview images in the .blend file " - "as well as a thumbnail of the .blend"); + prop = RNA_def_property(srna, "file_preview_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, preview_type_items); + RNA_def_property_ui_text(prop, "File Preview Type", "What type of blend preview to create"); rna_def_userdef_filepaths_asset_library(brna); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 8eaac47471a..dbc5a801cac 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1515,14 +1515,64 @@ static void wm_history_file_update(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Save Main Blend-File (internal) +/** \name Save Main Blend-File (internal) by capturing main window. + * \{ */ + +static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thumb_pt) +{ + if (*thumb_pt) { + /* We are given a valid thumbnail data, so just generate image from it. */ + return BKE_main_thumbnail_to_imbuf(NULL, *thumb_pt); + } + + /* Redraw to remove menus that might be open. */ + WM_redraw_windows(C); + WM_cursor_wait(true); + + /* The window to capture should be a main window (without parent). */ + wmWindow *win = CTX_wm_window(C); + while (win && win->parent) { + win = win->parent; + } + + int win_size[2]; + uint *buffer = WM_window_pixels_read(CTX_wm_manager(C), win, win_size); + ImBuf *ibuf = IMB_allocFromBuffer(buffer, NULL, win_size[0], win_size[1], 24); + + if (ibuf) { + int ex = (ibuf->x > ibuf->y) ? BLEN_THUMB_SIZE : + (int)((ibuf->x / (float)ibuf->y) * BLEN_THUMB_SIZE); + int ey = (ibuf->x > ibuf->y) ? (int)((ibuf->y / (float)ibuf->x) * BLEN_THUMB_SIZE) : + BLEN_THUMB_SIZE; + /* Filesystem thumbnail image can be 256x256. */ + IMB_scaleImBuf(ibuf, ex * 2, ey * 2); + + /* Thumbnail inside blend should be 128x128. */ + ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf); + IMB_scaleImBuf(thumb_ibuf, ex, ey); + + BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); + IMB_freeImBuf(thumb_ibuf); + MEM_freeN(buffer); + *thumb_pt = thumb; + } + WM_cursor_wait(false); + + /* Must be freed by caller. */ + return ibuf; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Save Main Blend-File (internal) by rendering scene * \{ */ /* screen can be NULL */ -static ImBuf *blend_file_thumb(const bContext *C, - Scene *scene, - bScreen *screen, - BlendThumbnail **thumb_pt) +static ImBuf *blend_file_thumb_from_camera(const bContext *C, + Scene *scene, + bScreen *screen, + BlendThumbnail **thumb_pt) { /* will be scaled down, but gives some nice oversampling */ ImBuf *ibuf; @@ -1704,8 +1754,13 @@ static bool wm_file_write(bContext *C, /* Main now can store a '.blend' thumbnail, useful for background mode * or thumbnail customization. */ main_thumb = thumb = bmain->blen_thumb; - if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) { - ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb); + if (BLI_thread_is_main()) { + if (U.file_preview_type == USER_FILE_PREVIEW_SCREENSHOT) { + ibuf_thumb = blend_file_thumb_from_screenshot(C, &thumb); + } + else if (U.file_preview_type == USER_FILE_PREVIEW_CAMERA) { + ibuf_thumb = blend_file_thumb_from_camera(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb); + } } /* operator now handles overwrite checks */ -- cgit v1.2.3 From 3e44592cb9f93b0f5b443c03c7d9103f28dba1a0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 15:51:53 +1000 Subject: Cleanup: comment unused functions --- source/blender/blenlib/BLI_string.h | 2 ++ source/blender/blenlib/intern/string.c | 20 +++++++++++++++++++- source/blender/makesrna/RNA_access.h | 2 ++ source/blender/makesrna/intern/rna_access.c | 4 +++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/BLI_string.h b/source/blender/blenlib/BLI_string.h index eab39433796..d3dc05edd9e 100644 --- a/source/blender/blenlib/BLI_string.h +++ b/source/blender/blenlib/BLI_string.h @@ -62,9 +62,11 @@ bool BLI_str_quoted_substr_range(const char *__restrict str, int *__restrict r_start, int *__restrict r_end) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 4); +#if 0 /* UNUSED */ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC; +#endif bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index fab5e44de25..0ea784c95b0 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -509,8 +509,12 @@ bool BLI_str_quoted_substr_range(const char *__restrict str, return true; } +/* NOTE(@campbellbarton): in principal it should be possible to access a quoted string + * with an arbitrary size, currently all callers for this functionality + * happened to use a fixed size buffer, so only #BLI_str_quoted_substr is needed. */ +#if 0 /** - * Makes a copy of the text within the "" that appear after some text `blahblah`. + * Makes a copy of the text within the "" that appear after the contents of \a prefix. * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. * * \param str: is the entire string to chop. @@ -533,7 +537,21 @@ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict } return result; } +#endif +/** + * Fills \a result with text within "" that appear after some the contents of \a prefix. + * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. + * + * \param str: is the entire string to chop. + * \param prefix: is the part of the string to step over. + * \param result: The buffer to fill. + * \param result_maxlen: The maximum size of the buffer (including nil terminator). + * \return True if the prefix was found and the entire quoted string was copied into result. + * + * Assume that the strings returned must be freed afterwards, + * and that the inputs will contain data we want. + */ bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index c2ea901f4a1..f5ebdbb569e 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1118,7 +1118,9 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop); char *RNA_path_append( const char *path, PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey); +#if 0 /* UNUSED. */ char *RNA_path_back(const char *path); +#endif /* path_resolve() variants only ensure that a valid pointer (and optionally property) exist */ bool RNA_path_resolve(PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 0ba5b786187..effc56b15c5 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5521,7 +5521,9 @@ char *RNA_path_append( return result; } -char *RNA_path_back(const char *path) +/* Having both path append & back seems like it could be useful, + * this function isn't used at the moment. */ +static char *UNUSED_FUNCTION(RNA_path_back)(const char *path) { char fixedbuf[256]; const char *previous, *current; -- cgit v1.2.3 From b4c9f88cbede8bc27d4d869232fd4a8f59e39f40 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 16:55:46 +1000 Subject: Fix thumbnail screenshot error in 58632a7f3c0f1be6cc860c7cad9c41ba43e6454f Scaling didn't clamp above zero, see T89868. --- source/blender/windowmanager/intern/wm_files.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index dbc5a801cac..30b76fd110b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1515,7 +1515,9 @@ static void wm_history_file_update(void) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Save Main Blend-File (internal) by capturing main window. +/** \name Save Main Blend-File (internal) Screen-Shot + * + * Screen-shot the active window. * \{ */ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thumb_pt) @@ -1540,11 +1542,17 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thu ImBuf *ibuf = IMB_allocFromBuffer(buffer, NULL, win_size[0], win_size[1], 24); if (ibuf) { - int ex = (ibuf->x > ibuf->y) ? BLEN_THUMB_SIZE : - (int)((ibuf->x / (float)ibuf->y) * BLEN_THUMB_SIZE); - int ey = (ibuf->x > ibuf->y) ? (int)((ibuf->y / (float)ibuf->x) * BLEN_THUMB_SIZE) : - BLEN_THUMB_SIZE; - /* Filesystem thumbnail image can be 256x256. */ + int ex, ey; + if (ibuf->x > ibuf->y) { + ex = BLEN_THUMB_SIZE; + ey = max_ii(1, (int)(((float)ibuf->y / (float)ibuf->x) * BLEN_THUMB_SIZE)); + } + else { + ex = max_ii(1, (int)(((float)ibuf->x / (float)ibuf->y) * BLEN_THUMB_SIZE)); + ey = BLEN_THUMB_SIZE; + } + + /* File-system thumbnail image can be 256x256. */ IMB_scaleImBuf(ibuf, ex * 2, ey * 2); /* Thumbnail inside blend should be 128x128. */ @@ -1565,7 +1573,9 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thu /** \} */ /* -------------------------------------------------------------------- */ -/** \name Save Main Blend-File (internal) by rendering scene +/** \name Save Main Blend-File (internal) Camera View + * + * Render the current scene with the active camera. * \{ */ /* screen can be NULL */ -- cgit v1.2.3 From 81978594a89a2e53c3d6626f51dfa85e94d6811d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 6 Sep 2021 10:56:02 +0200 Subject: Fix tests broken by rB58632a7f3c0f1be6. Commits breaking RNA API should always run all tests, and do text search in python code base to ensure everything is updated as needed. --- tests/python/bl_blendfile_io.py | 2 +- tests/python/bl_blendfile_liblink.py | 2 +- tests/python/bl_blendfile_library_overrides.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/python/bl_blendfile_io.py b/tests/python/bl_blendfile_io.py index 38b3a93bbbc..4123f06b7c4 100644 --- a/tests/python/bl_blendfile_io.py +++ b/tests/python/bl_blendfile_io.py @@ -73,7 +73,7 @@ def main(): args = argparse_create().parse_args() # Don't write thumbnails into the home directory. - bpy.context.preferences.filepaths.use_save_preview_images = False + bpy.context.preferences.filepaths.file_preview_type = 'NONE' for Test in TESTS: Test(args).run_all_tests() diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 1d076d66913..ac71fa85246 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -278,7 +278,7 @@ def main(): args = argparse_create().parse_args() # Don't write thumbnails into the home directory. - bpy.context.preferences.filepaths.use_save_preview_images = False + bpy.context.preferences.filepaths.file_preview_type = 'NONE' for Test in TESTS: Test(args).run_all_tests() diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index b44e4d48564..3c7c77ce339 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -208,7 +208,7 @@ def main(): args = argparse_create().parse_args() # Don't write thumbnails into the home directory. - bpy.context.preferences.filepaths.use_save_preview_images = False + bpy.context.preferences.filepaths.file_preview_type = 'NONE' bpy.context.preferences.experimental.use_override_templates = True for Test in TESTS: -- cgit v1.2.3 From 49f1695ed06792ea1f4d1a43d30170c407f227f8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 18:57:25 +1000 Subject: BLI_utildefines: add UNUSED_FUNCTION_WITH_RETURN_TYPE Unfortunately the UNUSED_FUNCTION macro doesn't work for pointer types. Add UNUSED_FUNCTION_WITH_RETURN_TYPE to workaround this limitation. --- source/blender/blenlib/BLI_utildefines.h | 10 ++++++++++ source/blender/makesrna/intern/rna_access.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 5b84e050f82..dec8acd7549 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -683,12 +683,22 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); # define UNUSED(x) UNUSED_##x #endif +/** + * WARNING: this doesn't warn when returning pointer types (because of the placement of `*`). + * Use #UNUSED_FUNCTION_WITH_RETURN_TYPE instead in this case. + */ #if defined(__GNUC__) || defined(__clang__) # define UNUSED_FUNCTION(x) __attribute__((__unused__)) UNUSED_##x #else # define UNUSED_FUNCTION(x) UNUSED_##x #endif +#if defined(__GNUC__) || defined(__clang__) +# define UNUSED_FUNCTION_WITH_RETURN_TYPE(rtype, x) __attribute__((__unused__)) rtype UNUSED_##x +#else +# define UNUSED_FUNCTION_WITH_RETURN_TYPE(rtype, x) rtype UNUSED_##x +#endif + /** * UNUSED_VARS#(a, ...): quiet unused warnings * diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index effc56b15c5..fceb6d045c3 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5523,7 +5523,7 @@ char *RNA_path_append( /* Having both path append & back seems like it could be useful, * this function isn't used at the moment. */ -static char *UNUSED_FUNCTION(RNA_path_back)(const char *path) +static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path) { char fixedbuf[256]; const char *previous, *current; -- cgit v1.2.3 From a0912ff5663b950222ef00485a3853bfb6001db4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 20:04:25 +1000 Subject: Cleanup: support passing in arbitrary buffers to IMB_allocFromBuffer Also remove IB_metadata flag from the resulting imbuf as this image has no meta-data. --- source/blender/blenkernel/intern/main.c | 7 ++----- source/blender/imbuf/intern/allocimbuf.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index bb33f5f9f87..981f1d4a623 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -405,11 +405,8 @@ ImBuf *BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data) } if (data) { - /* NOTE: we cannot use IMB_allocFromBuffer(), since it tries to dupalloc passed buffer, - * which will fail here (we do not want to pass the first two ints!). */ - img = IMB_allocImBuf( - (unsigned int)data->width, (unsigned int)data->height, 32, IB_rect | IB_metadata); - memcpy(img->rect, data->rect, BLEN_THUMB_MEMSIZE(data->width, data->height) - sizeof(*data)); + img = IMB_allocFromBuffer( + (const uint *)data->rect, NULL, (uint)data->width, (uint)data->height, 4); } return img; diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 90c863878ff..d327981f583 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -445,13 +445,21 @@ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, ibuf = IMB_allocImBuf(w, h, 32, 0); ibuf->channels = channels; + + /* Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation. */ if (rectf) { - ibuf->rect_float = MEM_dupallocN(rectf); + const size_t size = sizeof(float[4]) * w * h; + ibuf->rect_float = MEM_mallocN(sizeof(float[4]) * w * h, __func__); + memcpy(ibuf->rect_float, rectf, size); + ibuf->flags |= IB_rectfloat; ibuf->mall |= IB_rectfloat; } if (rect) { - ibuf->rect = MEM_dupallocN(rect); + const size_t size = sizeof(uchar[4]) * w * h; + ibuf->rect = MEM_mallocN(size, __func__); + memcpy(ibuf->rect, rect, size); + ibuf->flags |= IB_rect; ibuf->mall |= IB_rect; } -- cgit v1.2.3 From 687f70ceca88324fb8f376e086a55b341dd34e6f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 20:04:26 +1000 Subject: ImBuf: add IMB_allocFromBufferOwn that takes ownership of the buffer Avoids duplicating the image buffer when saving thumbnails. --- source/blender/blenkernel/intern/studiolight.c | 17 ++++++------- source/blender/imbuf/IMB_imbuf.h | 8 ++++++ source/blender/imbuf/intern/allocimbuf.c | 35 ++++++++++++++++++++++++++ source/blender/windowmanager/intern/wm_files.c | 3 +-- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 95436372a65..29f726ece71 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -439,17 +439,15 @@ static void studiolight_load_equirect_image(StudioLight *sl) if (ctx.diffuse_pass != NULL) { float *converted_pass = studiolight_multilayer_convert_pass( ibuf, ctx.diffuse_pass, ctx.num_diffuse_channels); - diffuse_ibuf = IMB_allocFromBuffer( + diffuse_ibuf = IMB_allocFromBufferOwn( NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_diffuse_channels); - MEM_freeN(converted_pass); } if (ctx.specular_pass != NULL) { float *converted_pass = studiolight_multilayer_convert_pass( ibuf, ctx.specular_pass, ctx.num_specular_channels); - specular_ibuf = IMB_allocFromBuffer( + specular_ibuf = IMB_allocFromBufferOwn( NULL, converted_pass, ibuf->x, ibuf->y, ctx.num_specular_channels); - MEM_freeN(converted_pass); } IMB_exr_close(ibuf->userdata); @@ -1148,12 +1146,11 @@ static void studiolight_calculate_irradiance_equirect_image(StudioLight *sl) } ITER_PIXELS_END; - sl->equirect_irradiance_buffer = IMB_allocFromBuffer(NULL, - colbuf, - STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, - STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT, - 4); - MEM_freeN(colbuf); + sl->equirect_irradiance_buffer = IMB_allocFromBufferOwn(NULL, + colbuf, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_WIDTH, + STUDIOLIGHT_IRRADIANCE_EQUIRECT_HEIGHT, + 4); } sl->flag |= STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED; } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 4ad7aa98484..dd8e6549e24 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -148,6 +148,14 @@ struct ImBuf *IMB_allocImBuf(unsigned int x, bool IMB_initImBuf( struct ImBuf *ibuf, unsigned int x, unsigned int y, unsigned char planes, unsigned int flags); +/** + * Create a copy of a pixel buffer and wrap it to a new ImBuf + * (transferring ownership to the in imbuf). + * \attention Defined in allocimbuf.c + */ +struct ImBuf *IMB_allocFromBufferOwn( + unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels); + /** * Create a copy of a pixel buffer and wrap it to a new ImBuf * \attention Defined in allocimbuf.c diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index d327981f583..c2bff9ddc51 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -430,6 +430,41 @@ bool imb_addrectImBuf(ImBuf *ibuf) return false; } +/** + * \param take_ownership: When true, the buffers become owned by the resulting image. + */ +struct ImBuf *IMB_allocFromBufferOwn( + unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels) +{ + ImBuf *ibuf = NULL; + + if (!(rect || rectf)) { + return NULL; + } + + ibuf = IMB_allocImBuf(w, h, 32, 0); + + ibuf->channels = channels; + + /* Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation. */ + if (rectf) { + BLI_assert(MEM_allocN_len(rectf) == sizeof(float[4]) * w * h); + ibuf->rect_float = rectf; + + ibuf->flags |= IB_rectfloat; + ibuf->mall |= IB_rectfloat; + } + if (rect) { + BLI_assert(MEM_allocN_len(rect) == sizeof(uchar[4]) * w * h); + ibuf->rect = rect; + + ibuf->flags |= IB_rect; + ibuf->mall |= IB_rect; + } + + return ibuf; +} + struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, const float *rectf, unsigned int w, diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 30b76fd110b..2b796e60cdc 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1539,7 +1539,7 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thu int win_size[2]; uint *buffer = WM_window_pixels_read(CTX_wm_manager(C), win, win_size); - ImBuf *ibuf = IMB_allocFromBuffer(buffer, NULL, win_size[0], win_size[1], 24); + ImBuf *ibuf = IMB_allocFromBufferOwn(buffer, NULL, win_size[0], win_size[1], 24); if (ibuf) { int ex, ey; @@ -1561,7 +1561,6 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **thu BlendThumbnail *thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); IMB_freeImBuf(thumb_ibuf); - MEM_freeN(buffer); *thumb_pt = thumb; } WM_cursor_wait(false); -- cgit v1.2.3 From 4f0ec3cea5732e5b06122519538488e104c7ff00 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 20:32:35 +1000 Subject: Cleanup: use pre-calculated size variable Oversight in a0912ff5663b950222ef00485a3853bfb6001db4 --- source/blender/imbuf/intern/allocimbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index c2bff9ddc51..1369f8cc0f8 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -484,7 +484,7 @@ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect, /* Avoid #MEM_dupallocN since the buffers might not be allocated using guarded-allocation. */ if (rectf) { const size_t size = sizeof(float[4]) * w * h; - ibuf->rect_float = MEM_mallocN(sizeof(float[4]) * w * h, __func__); + ibuf->rect_float = MEM_mallocN(size, __func__); memcpy(ibuf->rect_float, rectf, size); ibuf->flags |= IB_rectfloat; -- cgit v1.2.3 From 4d0497bea44d10668a160d0f3882c6f11e7af587 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 6 Sep 2021 20:33:33 +1000 Subject: Cleanup: format, spelling --- source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/windowmanager/intern/wm_files.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index b519a6014f0..1cb561ddd9f 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -927,7 +927,7 @@ typedef struct UserDef { char _pad10[2]; char file_preview_type; /* eUserpref_File_Preview_Type */ - char statusbar_flag; /* eUserpref_StatusBar_Flag */ + char statusbar_flag; /* eUserpref_StatusBar_Flag */ struct WalkNavigation walk_navigation; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 2b796e60cdc..23c91dface9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1675,7 +1675,7 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C, IMB_scaleImBuf(thumb_ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE); thumb = BKE_main_thumbnail_from_imbuf(NULL, thumb_ibuf); IMB_freeImBuf(thumb_ibuf); - /* Thumbnail saved to filesystem should be 256x256. */ + /* Thumbnail saved to file-system should be 256x256. */ IMB_scaleImBuf(ibuf, PREVIEW_RENDER_LARGE_HEIGHT, PREVIEW_RENDER_LARGE_HEIGHT); } else { -- cgit v1.2.3 From e2bbb5b07edba855b3360ecd6ecd9b6bdb0646f1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 6 Sep 2021 12:54:51 +0200 Subject: BLI: add default hash for shared_ptr and reference_wrapper This makes it easier to use these types as keys in Map, Set and VectorSet. --- source/blender/blenlib/BLI_hash.hh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh index fbed321534c..11ff7d040aa 100644 --- a/source/blender/blenlib/BLI_hash.hh +++ b/source/blender/blenlib/BLI_hash.hh @@ -250,6 +250,20 @@ template struct DefaultHash> { } }; +template struct DefaultHash> { + uint64_t operator()(const std::shared_ptr &value) const + { + return get_default_hash(value.get()); + } +}; + +template struct DefaultHash> { + uint64_t operator()(const std::reference_wrapper &value) const + { + return get_default_hash(value.get()); + } +}; + template struct DefaultHash> { uint64_t operator()(const std::pair &value) const { -- cgit v1.2.3 From 5eed7cdc8c17b96ba9fd38647e86d412e682e137 Mon Sep 17 00:00:00 2001 From: William Leeson Date: Mon, 6 Sep 2021 13:08:41 +0200 Subject: Division by zero when there are no lights and only emissive surfaces When rendering the test scene in T79190 which has only emissive surfaces a division by zero occurs. This is a simple patch to remove this. Reviewed By: brecht Maniphest Tasks: T79190 Differential Revision: https://developer.blender.org/D11682 --- intern/cycles/render/light.cpp | 53 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 5290d68e75a..5f9764cd1cc 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -410,38 +410,39 @@ void LightManager::device_update_distribution(Device *, } float trianglearea = totarea; - /* point lights */ - float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f; bool use_lamp_mis = false; - int light_index = 0; - foreach (Light *light, scene->lights) { - if (!light->is_enabled) - continue; + + if (num_lights > 0) { + float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f; + foreach (Light *light, scene->lights) { + if (!light->is_enabled) + continue; - distribution[offset].totarea = totarea; - distribution[offset].prim = ~light_index; - distribution[offset].lamp.pad = 1.0f; - distribution[offset].lamp.size = light->size; - totarea += lightarea; + distribution[offset].totarea = totarea; + distribution[offset].prim = ~light_index; + distribution[offset].lamp.pad = 1.0f; + distribution[offset].lamp.size = light->size; + totarea += lightarea; - if (light->light_type == LIGHT_DISTANT) { - use_lamp_mis |= (light->angle > 0.0f && light->use_mis); - } - else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) { - use_lamp_mis |= (light->size > 0.0f && light->use_mis); - } - else if (light->light_type == LIGHT_AREA) { - use_lamp_mis |= light->use_mis; - } - else if (light->light_type == LIGHT_BACKGROUND) { - num_background_lights++; - background_mis |= light->use_mis; - } + if (light->light_type == LIGHT_DISTANT) { + use_lamp_mis |= (light->angle > 0.0f && light->use_mis); + } + else if (light->light_type == LIGHT_POINT || light->light_type == LIGHT_SPOT) { + use_lamp_mis |= (light->size > 0.0f && light->use_mis); + } + else if (light->light_type == LIGHT_AREA) { + use_lamp_mis |= light->use_mis; + } + else if (light->light_type == LIGHT_BACKGROUND) { + num_background_lights++; + background_mis |= light->use_mis; + } - light_index++; - offset++; + light_index++; + offset++; + } } /* normalize cumulative distribution functions */ -- cgit v1.2.3 From 861b7071a24be8ef052570b11c044ad0f8df7f3f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 6 Sep 2021 15:47:44 +0200 Subject: Fix crash drawing hair with older GPUs. Some GPU's have support for compute shaders, but don't support GLSL 4.3. This resulted in compiler errors and crashes. This issue could have been solved by supporting older GLSL languages but that would have been a hassle to get it right. We already have a fallback in place for GPU's that don't support compute shaders at all. --- source/blender/gpu/opengl/gl_backend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 772fc19d919..2855d5078ff 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -460,7 +460,7 @@ void GLBackend::capabilities_init() GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo; GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store; - GCaps.compute_shader_support = GLEW_ARB_compute_shader; + GCaps.compute_shader_support = GLEW_ARB_compute_shader && GLEW_VERSION_4_3; if (GCaps.compute_shader_support) { glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]); glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]); -- cgit v1.2.3 From a3ca973dec752687dbbf5c3b24b51404ddfdf614 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 6 Sep 2021 17:21:27 +0200 Subject: Nodes: fix incorrect id socket update The issue was that the entire socket was rebuild, even though only its `SOCK_HIDE_LABEL` flag changed. This broke e.g. Object sockets from old files. --- source/blender/nodes/NOD_socket_declarations.hh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 639d2c12d73..1a5873c37b5 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -199,6 +199,20 @@ template class IDSocketDeclaration : public SocketDeclaration { return matches_id_socket(socket, data_, name_, identifier_); } + + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override + { + if (StringRef(socket.idname) != data_.idname) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + if (data_.hide_label) { + socket.flag |= SOCK_HIDE_LABEL; + } + else { + socket.flag &= ~SOCK_HIDE_LABEL; + } + return socket; + } }; } // namespace detail -- cgit v1.2.3 From 3e23af4c4915f1ea5cb61c60e58cdfba41df1b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Mon, 6 Sep 2021 17:17:31 +0200 Subject: Fluid: Clang-format cleanups Just cleanup. --- extern/mantaflow/UPDATE.sh | 2 +- extern/mantaflow/helper/pwrapper/pconvert.cpp | 6 ++++-- extern/mantaflow/helper/pwrapper/pvec3.cpp | 6 ++++-- extern/mantaflow/helper/pwrapper/registry.cpp | 3 ++- extern/mantaflow/preprocessed/gitinfo.h | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/extern/mantaflow/UPDATE.sh b/extern/mantaflow/UPDATE.sh index aed4e2a9b71..1158ff13455 100644 --- a/extern/mantaflow/UPDATE.sh +++ b/extern/mantaflow/UPDATE.sh @@ -8,7 +8,7 @@ # YOUR INSTALLATION PATHS GO HERE: MANTA_INSTALLATION=/Users/sebbas/Developer/Mantaflow/mantaflowDevelop -BLENDER_INSTALLATION=/Users/sebbas/Developer/Blender/fluid-mantaflow +BLENDER_INSTALLATION=/Users/sebbas/Developer/Blender # Try to check out Mantaflow repository before building? CLEAN_REPOSITORY=0 diff --git a/extern/mantaflow/helper/pwrapper/pconvert.cpp b/extern/mantaflow/helper/pwrapper/pconvert.cpp index 7c66cdc7e72..5a7a32c5a73 100644 --- a/extern/mantaflow/helper/pwrapper/pconvert.cpp +++ b/extern/mantaflow/helper/pwrapper/pconvert.cpp @@ -28,11 +28,13 @@ extern PyTypeObject PbVec3Type; extern PyTypeObject PbVec4Type; struct PbVec3 { - PyObject_HEAD float data[3]; + PyObject_HEAD + float data[3]; }; struct PbVec4 { - PyObject_HEAD float data[4]; + PyObject_HEAD + float data[4]; }; PyObject *getPyNone() diff --git a/extern/mantaflow/helper/pwrapper/pvec3.cpp b/extern/mantaflow/helper/pwrapper/pvec3.cpp index 1dca44d5e5c..4d07a201cfe 100644 --- a/extern/mantaflow/helper/pwrapper/pvec3.cpp +++ b/extern/mantaflow/helper/pwrapper/pvec3.cpp @@ -25,7 +25,8 @@ namespace Manta { extern PyTypeObject PbVec3Type; struct PbVec3 { - PyObject_HEAD float data[3]; + PyObject_HEAD + float data[3]; }; static void PbVec3Dealloc(PbVec3 *self) @@ -293,7 +294,8 @@ inline PyObject *castPy(PyTypeObject *p) extern PyTypeObject PbVec4Type; struct PbVec4 { - PyObject_HEAD float data[4]; + PyObject_HEAD + float data[4]; }; static PyMethodDef PbVec4Methods[] = { diff --git a/extern/mantaflow/helper/pwrapper/registry.cpp b/extern/mantaflow/helper/pwrapper/registry.cpp index f88c2aa708e..5196c0409f8 100644 --- a/extern/mantaflow/helper/pwrapper/registry.cpp +++ b/extern/mantaflow/helper/pwrapper/registry.cpp @@ -76,7 +76,8 @@ struct ClassData { }; struct PbObject { - PyObject_HEAD Manta::PbClass *instance; + PyObject_HEAD + Manta::PbClass *instance; ClassData *classdef; }; diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 6bc92278a33..6d367b764af 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit 8fbebe02459b7f72575872c20961f7cb757db408" +#define MANTA_GIT_VERSION "commit d5d9a6c28daa8f21426d7a285f48639c0d8fd13f" -- cgit v1.2.3 From 5a02d0da7a88e31422b61d6e4e69c9f464290241 Mon Sep 17 00:00:00 2001 From: Erik Abrahamsson Date: Mon, 6 Sep 2021 17:26:55 +0200 Subject: Fluid: Parallelizations for Mantaflow functions (D12002) This update includes part of a performance boost from D12002. Contributed by @erik85 --- extern/mantaflow/preprocessed/fastmarch.cpp | 202 +++++++++++++++++++--------- 1 file changed, 140 insertions(+), 62 deletions(-) diff --git a/extern/mantaflow/preprocessed/fastmarch.cpp b/extern/mantaflow/preprocessed/fastmarch.cpp index 956725e523c..31e43483b49 100644 --- a/extern/mantaflow/preprocessed/fastmarch.cpp +++ b/extern/mantaflow/preprocessed/fastmarch.cpp @@ -874,6 +874,136 @@ static const Vec3i nb[6] = {Vec3i(1, 0, 0), Vec3i(0, 0, 1), Vec3i(0, 0, -1)}; +struct knMarkSkipCells : public KernelBase { + knMarkSkipCells(Grid &phi, Grid &tmp, bool inside) + : KernelBase(&phi, 1), phi(phi), tmp(tmp), inside(inside) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, Grid &phi, Grid &tmp, bool inside) const + { + if (!inside && phi(i, j, k) < 0.) { + tmp(i, j, k) = 1; + } + if (inside && phi(i, j, k) > 0.) { + tmp(i, j, k) = 1; + } + } + inline Grid &getArg0() + { + return phi; + } + typedef Grid type0; + inline Grid &getArg1() + { + return tmp; + } + typedef Grid type1; + inline bool &getArg2() + { + return inside; + } + typedef bool type2; + void runMessage() + { + debMsg("Executing kernel knMarkSkipCells ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, phi, tmp, inside); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, phi, tmp, inside); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(1, maxY), *this); + } + Grid φ + Grid &tmp; + bool inside; +}; + +struct knSetFirstLayer : public KernelBase { + knSetFirstLayer(Grid &tmp, int dim) : KernelBase(&tmp, 1), tmp(tmp), dim(dim) + { + runMessage(); + run(); + } + inline void op(int i, int j, int k, Grid &tmp, int dim) const + { + Vec3i p(i, j, k); + if (tmp(p)) + return; + for (int n = 0; n < 2 * dim; ++n) { + if (tmp(p + nb[n]) == 1) { + tmp(i, j, k) = 2; + break; + } + } + } + inline Grid &getArg0() + { + return tmp; + } + typedef Grid type0; + inline int &getArg1() + { + return dim; + } + typedef int type1; + void runMessage() + { + debMsg("Executing kernel knSetFirstLayer ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, tmp, dim); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, tmp, dim); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(1, maxY), *this); + } + Grid &tmp; + int dim; +}; + template struct knExtrapolateLsSimple : public KernelBase { knExtrapolateLsSimple(Grid &val, int distance, Grid &tmp, const int d, S direction) : KernelBase(&val, 1), val(val), distance(distance), tmp(tmp), d(d), direction(direction) @@ -1043,39 +1173,12 @@ void extrapolateLsSimple(Grid &phi, int distance = 4, bool inside = false) tmp.clear(); const int dim = (phi.is3D() ? 3 : 2); - // by default, march outside - Real direction = 1.; - if (!inside) { - // mark all inside - FOR_IJK_BND(phi, 1) - { - if (phi(i, j, k) < 0.) { - tmp(i, j, k) = 1; - } - } - } - else { - direction = -1.; - FOR_IJK_BND(phi, 1) - { - if (phi(i, j, k) > 0.) { - tmp(i, j, k) = 1; - } - } - } + // by default, march outside (ie mark all inside to be skipped) + Real direction = (inside) ? -1. : 1.; + knMarkSkipCells(phi, tmp, inside); + // + first layer around - FOR_IJK_BND(phi, 1) - { - Vec3i p(i, j, k); - if (tmp(p)) - continue; - for (int n = 0; n < 2 * dim; ++n) { - if (tmp(p + nb[n]) == 1) { - tmp(i, j, k) = 2; - n = 2 * dim; - } - } - } + knSetFirstLayer(tmp, dim); // extrapolate for distance for (int d = 2; d < 1 + distance; ++d) { @@ -1126,37 +1229,12 @@ void extrapolateVec3Simple(Grid &vel, Grid &phi, int distance = 4, b tmp.clear(); const int dim = (vel.is3D() ? 3 : 2); - // mark initial cells, by default, march outside - if (!inside) { - // mark all inside - FOR_IJK_BND(phi, 1) - { - if (phi(i, j, k) < 0.) { - tmp(i, j, k) = 1; - } - } - } - else { - FOR_IJK_BND(phi, 1) - { - if (phi(i, j, k) > 0.) { - tmp(i, j, k) = 1; - } - } - } + // mark initial cells, by default, march outside (ie mark all inside to be skipped) + Real direction = (inside) ? -1. : 1.; + knMarkSkipCells(phi, tmp, inside); + // + first layer next to initial cells - FOR_IJK_BND(vel, 1) - { - Vec3i p(i, j, k); - if (tmp(p)) - continue; - for (int n = 0; n < 2 * dim; ++n) { - if (tmp(p + nb[n]) == 1) { - tmp(i, j, k) = 2; - n = 2 * dim; - } - } - } + knSetFirstLayer(tmp, dim); for (int d = 2; d < 1 + distance; ++d) { knExtrapolateLsSimple(vel, distance, tmp, d, Vec3(0.)); -- cgit v1.2.3 From ce71357711eb0d79916045f1e67523af21b1791e Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 6 Sep 2021 17:34:59 +0200 Subject: Fix T90414: New GPencil icons Designed by @mendio The new icons are: * Dot-Dash modifier * Length Modifier * Line Art modifier --- release/datafiles/blender_icons.svg | 52 +++++++++++++++++++++ .../datafiles/blender_icons16/icon16_mod_dash.dat | Bin 0 -> 1048 bytes .../blender_icons16/icon16_mod_length.dat | Bin 0 -> 1048 bytes .../blender_icons16/icon16_mod_lineart.dat | Bin 0 -> 1048 bytes .../datafiles/blender_icons32/icon32_mod_dash.dat | Bin 0 -> 4120 bytes .../blender_icons32/icon32_mod_length.dat | Bin 0 -> 4120 bytes .../blender_icons32/icon32_mod_lineart.dat | Bin 0 -> 4120 bytes source/blender/editors/datafiles/CMakeLists.txt | 3 ++ source/blender/editors/include/UI_icons.h | 6 +-- 9 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 release/datafiles/blender_icons16/icon16_mod_dash.dat create mode 100644 release/datafiles/blender_icons16/icon16_mod_length.dat create mode 100644 release/datafiles/blender_icons16/icon16_mod_lineart.dat create mode 100644 release/datafiles/blender_icons32/icon32_mod_dash.dat create mode 100644 release/datafiles/blender_icons32/icon32_mod_length.dat create mode 100644 release/datafiles/blender_icons32/icon32_mod_lineart.dat diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index c3461fd1bea..70bd7dc8085 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -17321,6 +17321,58 @@ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:fill markers stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:new" d="m 368.30892,141.58547 c -0.27613,4e-5 -0.49997,0.22388 -0.5,0.5 v 1.26473 h -4.76715 v 1.4911 h 4.76715 v 1.24417 c 3e-5,0.27613 0.22387,0.49997 0.5,0.5 0.63583,0.004 3.43318,-0.006 3.9995,-0.006 0.24106,0 0.46127,-0.0485 0.46127,-0.50967 4e-5,-0.85242 -8.9e-4,-2.98571 -8.9e-4,-3.95935 0,-0.30244 -0.19636,-0.51552 -0.46153,-0.51552 -0.82724,0 -3.36276,-0.009 -3.99823,-0.009 v 2e-5 z m 2.30359,-4.68113 -0.005,4.25868 c 0.48989,0.002 1.39549,0.005 1.88538,0.007 0.44541,0.0357 0.71675,0.47423 0.71675,0.85988 -6.6e-4,1.00616 -0.009,2.97018 -0.009,4.15122 0,0.46073 -0.24756,0.84994 -0.6533,0.84994 -0.48399,0.0143 -1.44986,-1.1e-4 -1.93405,-1.6e-4 v 3.87356 l -7.75691,-0.0669 v -14.00001 z" sodipodi:nodetypes="cccccccccccccccccccccccccc" /> + + + + + + + + + + + + + Date: Mon, 6 Sep 2021 17:58:38 +0200 Subject: make.bat: Fix missing quotes in python detection --- build_files/windows/icons.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/windows/icons.cmd b/build_files/windows/icons.cmd index 473a40885a8..d51b27d8953 100644 --- a/build_files/windows/icons.cmd +++ b/build_files/windows/icons.cmd @@ -1,4 +1,4 @@ -if EXIST %PYTHON% ( +if EXIST "%PYTHON%" ( goto detect_python_done ) -- cgit v1.2.3 From d9ad77fa58eb6301e3fedc709c946b7349c057b2 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 6 Sep 2021 18:00:50 +0200 Subject: GPencil: Replace temp icons for final design New icons for LIneArt and Length modifier --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4e95174e42b..06e8f7db0af 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -70,7 +70,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { "Create duplication of strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", - ICON_MOD_EDGESPLIT, /* TODO: Use a proper icon. */ + ICON_MOD_LINEART, "Line Art", "Generate line art strokes from selected source"}, {eGpencilModifierType_Mirror, @@ -116,7 +116,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { "Deform strokes using lattice"}, {eGpencilModifierType_Length, "GP_LENGTH", - ICON_MOD_EDGESPLIT, + ICON_MOD_LENGTH, "Length", "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, -- cgit v1.2.3 From 5a9a16334c573c4566dc9b2a314cf0d0ccdcb54f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 6 Sep 2021 18:22:24 +0200 Subject: Geometry Nodes: support for geometry instancing Previously, the Point Instance node in geometry nodes could only instance existing objects or collections. The reason was that large parts of Blender worked under the assumption that objects are the main unit of instancing. Now we also want to instance geometry within an object, so a slightly larger refactor was necessary. This should not affect files that do not use the new kind of instances. The main change is a redefinition of what "instanced data" is. Now, an instances is a cow-object + object-data (the geometry). This can be nicely seen in `struct DupliObject`. This allows the same object to generate multiple geometries of different types which can be instanced individually. A nice side effect of this refactor is that having multiple geometry components is not a special case in the depsgraph object iterator anymore, because those components are integrated with the `DupliObject` system. Unfortunately, different systems that work with instances in Blender (e.g. render engines and exporters) often work under the assumption that objects are the main unit of instancing. So those have to be updated as well to be able to handle the new instances. This patch updates Cycles, EEVEE and other viewport engines. Exporters have not been updated yet. Some minimal (not master-ready) changes to update the obj and alembic exporters can be found in P2336 and P2335. Different file formats may want to handle these new instances in different ways. For users, the only thing that changed is that the Point Instance node now has a geometry mode. This also fixes T88454. Differential Revision: https://developer.blender.org/D11841 --- intern/cycles/blender/blender_curves.cpp | 42 ++++--- intern/cycles/blender/blender_geometry.cpp | 42 +++---- intern/cycles/blender/blender_light.cpp | 15 ++- intern/cycles/blender/blender_mesh.cpp | 61 +++++---- intern/cycles/blender/blender_object.cpp | 26 ++-- intern/cycles/blender/blender_sync.h | 29 +++-- intern/cycles/blender/blender_util.h | 49 ++++++-- intern/cycles/blender/blender_volume.cpp | 21 ++-- source/blender/blenkernel/BKE_duplilist.h | 4 + source/blender/blenkernel/BKE_geometry_set.h | 2 +- source/blender/blenkernel/BKE_geometry_set.hh | 45 ++++++- source/blender/blenkernel/BKE_object.h | 2 + .../intern/geometry_component_instances.cc | 15 ++- source/blender/blenkernel/intern/geometry_set.cc | 37 +++++- .../blenkernel/intern/geometry_set_instances.cc | 12 ++ source/blender/blenkernel/intern/object.c | 13 +- source/blender/blenkernel/intern/object_dupli.cc | 97 ++++++++++++--- source/blender/blenlib/BLI_user_counter.hh | 12 ++ source/blender/depsgraph/DEG_depsgraph_query.h | 10 +- .../depsgraph/intern/depsgraph_query_iter.cc | 138 ++------------------- source/blender/draw/intern/draw_manager.c | 64 ++++++++-- source/blender/draw/intern/draw_manager.h | 15 ++- source/blender/editors/object/object_add.c | 10 +- .../space_spreadsheet/spreadsheet_cell_value.hh | 5 + .../spreadsheet_data_source_geometry.cc | 5 + .../space_spreadsheet/spreadsheet_layout.cc | 17 +++ .../editors/transform/transform_snap_object.c | 4 +- source/blender/makesdna/DNA_node_types.h | 1 + source/blender/makesrna/intern/rna_nodetree.c | 5 + .../geometry/nodes/node_geo_point_instance.cc | 17 ++- 30 files changed, 501 insertions(+), 314 deletions(-) diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 85d886fd850..6fe5ea41fff 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -526,8 +526,13 @@ bool BlenderSync::object_has_particle_hair(BL::Object b_ob) /* Old particle hair. */ void BlenderSync::sync_particle_hair( - Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step) + Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step) { + if (!b_ob_info.is_real_object_data()) { + return; + } + BL::Object b_ob = b_ob_info.real_object; + /* obtain general settings */ if (b_ob.mode() == b_ob.mode_PARTICLE_EDIT || b_ob.mode() == b_ob.mode_EDIT) { return; @@ -788,10 +793,10 @@ static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_st } /* Hair object. */ -void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step) +void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step) { /* Convert Blender hair to Cycles curves. */ - BL::Hair b_hair(b_ob.data()); + BL::Hair b_hair(b_ob_info.object_data); if (motion) { export_hair_curves_motion(hair, b_hair, motion_step); } @@ -800,16 +805,16 @@ void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motio } } #else -void BlenderSync::sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step) +void BlenderSync::sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step) { (void)hair; - (void)b_ob; + (void)b_ob_info; (void)motion; (void)motion_step; } #endif -void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair) +void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair) { /* make a copy of the shaders as the caller in the main thread still need them for syncing the * attributes */ @@ -819,19 +824,19 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha new_hair.set_used_shaders(used_shaders); if (view_layer.use_hair) { - if (b_ob.type() == BL::Object::type_HAIR) { + if (b_ob_info.object_data.is_a(&RNA_Hair)) { /* Hair object. */ - sync_hair(&new_hair, b_ob, false); + sync_hair(&new_hair, b_ob_info, false); } else { /* Particle hair. */ bool need_undeformed = new_hair.need_attribute(scene, ATTR_STD_GENERATED); BL::Mesh b_mesh = object_to_mesh( - b_data, b_ob, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE); + b_data, b_ob_info, b_depsgraph, need_undeformed, Mesh::SUBDIVISION_NONE); if (b_mesh) { - sync_particle_hair(&new_hair, b_mesh, b_ob, false); - free_object_to_mesh(b_data, b_ob, b_mesh); + sync_particle_hair(&new_hair, b_mesh, b_ob_info, false); + free_object_to_mesh(b_data, b_ob_info, b_mesh); } } } @@ -859,7 +864,7 @@ void BlenderSync::sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *ha } void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph, - BL::Object b_ob, + BObjectInfo &b_ob_info, Hair *hair, int motion_step) { @@ -869,18 +874,19 @@ void BlenderSync::sync_hair_motion(BL::Depsgraph b_depsgraph, } /* Export deformed coordinates. */ - if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { - if (b_ob.type() == BL::Object::type_HAIR) { + if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { + if (b_ob_info.object_data.is_a(&RNA_Hair)) { /* Hair object. */ - sync_hair(hair, b_ob, true, motion_step); + sync_hair(hair, b_ob_info, true, motion_step); return; } else { /* Particle hair. */ - BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE); + BL::Mesh b_mesh = object_to_mesh( + b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE); if (b_mesh) { - sync_particle_hair(hair, b_mesh, b_ob, true, motion_step); - free_object_to_mesh(b_data, b_ob, b_mesh); + sync_particle_hair(hair, b_mesh, b_ob_info, true, motion_step); + free_object_to_mesh(b_data, b_ob_info, b_mesh); return; } } diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index a009018f357..acc089a286c 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -29,13 +29,15 @@ CCL_NAMESPACE_BEGIN -static Geometry::Type determine_geom_type(BL::Object &b_ob, bool use_particle_hair) +static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair) { - if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) { + if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) { return Geometry::HAIR; } - if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) { + if (b_ob_info.object_data.is_a(&RNA_Volume) || + (b_ob_info.object_data == b_ob_info.real_object.data() && + object_fluid_gas_domain_find(b_ob_info.real_object))) { return Geometry::VOLUME; } @@ -71,20 +73,17 @@ array BlenderSync::find_used_shaders(BL::Object &b_ob) } Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, - BL::Object &b_ob, - BL::Object &b_ob_instance, + BObjectInfo &b_ob_info, bool object_updated, bool use_particle_hair, TaskPool *task_pool) { /* Test if we can instance or if the object is modified. */ - BL::ID b_ob_data = b_ob.data(); - BL::ID b_key_id = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data; - Geometry::Type geom_type = determine_geom_type(b_ob, use_particle_hair); - GeometryKey key(b_key_id.ptr.data, geom_type); + Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair); + GeometryKey key(b_ob_info.object_data, geom_type); /* Find shader indices. */ - array used_shaders = find_used_shaders(b_ob); + array used_shaders = find_used_shaders(b_ob_info.iter_object); /* Ensure we only sync instanced geometry once. */ Geometry *geom = geometry_map.find(key); @@ -111,7 +110,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, } else { /* Test if we need to update existing geometry. */ - sync = geometry_map.update(geom, b_key_id); + sync = geometry_map.update(geom, b_ob_info.object_data); } if (!sync) { @@ -144,7 +143,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, geometry_synced.insert(geom); - geom->name = ustring(b_ob_data.name().c_str()); + geom->name = ustring(b_ob_info.object_data.name().c_str()); /* Store the shaders immediately for the object attribute code. */ geom->set_used_shaders(used_shaders); @@ -153,19 +152,19 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, if (progress.get_cancel()) return; - progress.set_sync_status("Synchronizing object", b_ob.name()); + progress.set_sync_status("Synchronizing object", b_ob_info.real_object.name()); if (geom_type == Geometry::HAIR) { Hair *hair = static_cast(geom); - sync_hair(b_depsgraph, b_ob, hair); + sync_hair(b_depsgraph, b_ob_info, hair); } else if (geom_type == Geometry::VOLUME) { Volume *volume = static_cast(geom); - sync_volume(b_ob, volume); + sync_volume(b_ob_info, volume); } else { Mesh *mesh = static_cast(geom); - sync_mesh(b_depsgraph, b_ob, mesh); + sync_mesh(b_depsgraph, b_ob_info, mesh); } }; @@ -181,7 +180,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, } void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, - BL::Object &b_ob, + BObjectInfo &b_ob_info, Object *object, float motion_time, bool use_particle_hair, @@ -210,16 +209,17 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, if (progress.get_cancel()) return; - if (b_ob.type() == BL::Object::type_HAIR || use_particle_hair) { + if (b_ob_info.object_data.is_a(&RNA_Hair) || use_particle_hair) { Hair *hair = static_cast(geom); - sync_hair_motion(b_depsgraph, b_ob, hair, motion_step); + sync_hair_motion(b_depsgraph, b_ob_info, hair, motion_step); } - else if (b_ob.type() == BL::Object::type_VOLUME || object_fluid_gas_domain_find(b_ob)) { + else if (b_ob_info.object_data.is_a(&RNA_Volume) || + object_fluid_gas_domain_find(b_ob_info.real_object)) { /* No volume motion blur support yet. */ } else { Mesh *mesh = static_cast(geom); - sync_mesh_motion(b_depsgraph, b_ob, mesh, motion_step); + sync_mesh_motion(b_depsgraph, b_ob_info, mesh, motion_step); } }; diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/blender_light.cpp index 50cd9e3db5c..542028f4b2f 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/blender_light.cpp @@ -27,15 +27,14 @@ CCL_NAMESPACE_BEGIN void BlenderSync::sync_light(BL::Object &b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], - BL::Object &b_ob, - BL::Object &b_ob_instance, + BObjectInfo &b_ob_info, int random_id, Transform &tfm, bool *use_portal) { /* test if we need to sync */ - ObjectKey key(b_parent, persistent_id, b_ob_instance, false); - BL::Light b_light(b_ob.data()); + ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, false); + BL::Light b_light(b_ob_info.object_data); Light *light = light_map.find(key); @@ -44,7 +43,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, const bool tfm_updated = (light && light->get_tfm() != tfm); /* Update if either object or light data changed. */ - if (!light_map.add_or_update(&light, b_ob, b_parent, key) && !tfm_updated) { + if (!light_map.add_or_update(&light, b_ob_info.real_object, b_parent, key) && !tfm_updated) { Shader *shader; if (!shader_map.add_or_update(&shader, b_light)) { if (light->get_is_portal()) @@ -139,11 +138,11 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_max_bounces(get_int(clight, "max_bounces")); - if (b_ob != b_ob_instance) { + if (b_ob_info.real_object != b_ob_info.iter_object) { light->set_random_id(random_id); } else { - light->set_random_id(hash_uint2(hash_string(b_ob.name().c_str()), 0)); + light->set_random_id(hash_uint2(hash_string(b_ob_info.real_object.name().c_str()), 0)); } if (light->get_light_type() == LIGHT_AREA) @@ -155,7 +154,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, *use_portal = true; /* visibility */ - uint visibility = object_ray_visibility(b_ob); + uint visibility = object_ray_visibility(b_ob_info.real_object); light->set_use_diffuse((visibility & PATH_RAY_DIFFUSE) != 0); light->set_use_glossy((visibility & PATH_RAY_GLOSSY) != 0); light->set_use_transmission((visibility & PATH_RAY_TRANSMIT) != 0); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index ebba6981502..9bb3447f56b 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -999,12 +999,14 @@ static void create_mesh(Scene *scene, static void create_subd_mesh(Scene *scene, Mesh *mesh, - BL::Object &b_ob, + BObjectInfo &b_ob_info, BL::Mesh &b_mesh, const array &used_shaders, float dicing_rate, int max_subdivisions) { + BL::Object b_ob = b_ob_info.real_object; + BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length() - 1]); bool subdivide_uvs = subsurf_mod.uv_smooth() != BL::SubsurfModifier::uv_smooth_NONE; @@ -1043,7 +1045,7 @@ static void create_subd_mesh(Scene *scene, * * NOTE: This code is run prior to object motion blur initialization. so can not access properties * set by `sync_object_motion_init()`. */ -static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene) +static bool mesh_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene) { const Scene::MotionType need_motion = scene->need_motion(); if (need_motion == Scene::MOTION_NONE) { @@ -1060,7 +1062,7 @@ static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene) * - Motion attribute expects non-zero time steps. * * Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */ - PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); + PointerRNA cobject = RNA_pointer_get(&b_ob_info.real_object.ptr, "cycles"); const bool use_motion = get_boolean(cobject, "use_motion_blur"); if (!use_motion) { return false; @@ -1072,12 +1074,13 @@ static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene) return true; } -static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh) +static void sync_mesh_cached_velocities(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh) { - if (!mesh_need_motion_attribute(b_ob, scene)) { + if (!mesh_need_motion_attribute(b_ob_info, scene)) { return; } + BL::Object b_ob = b_ob_info.real_object; BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); if (!b_mesh_cache) { @@ -1118,12 +1121,16 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me } } -static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh) +static void sync_mesh_fluid_motion(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh) { - if (!mesh_need_motion_attribute(b_ob, scene)) { + if (!b_ob_info.is_real_object_data()) { + return; + } + if (!mesh_need_motion_attribute(b_ob_info, scene)) { return; } + BL::Object b_ob = b_ob_info.real_object; BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob); if (!b_fluid_domain) @@ -1157,7 +1164,7 @@ static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh) } } -void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh) +void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh) { /* make a copy of the shaders as the caller in the main thread still need them for syncing the * attributes */ @@ -1170,20 +1177,21 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me /* Adaptive subdivision setup. Not for baking since that requires * exact mapping to the Blender mesh. */ if (!scene->bake_manager->get_baking()) { - new_mesh.set_subdivision_type(object_subdivision_type(b_ob, preview, experimental)); + new_mesh.set_subdivision_type( + object_subdivision_type(b_ob_info.real_object, preview, experimental)); } /* For some reason, meshes do not need this... */ bool need_undeformed = new_mesh.need_attribute(scene, ATTR_STD_GENERATED); BL::Mesh b_mesh = object_to_mesh( - b_data, b_ob, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type()); + b_data, b_ob_info, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type()); if (b_mesh) { /* Sync mesh itself. */ if (new_mesh.get_subdivision_type() != Mesh::SUBDIVISION_NONE) create_subd_mesh(scene, &new_mesh, - b_ob, + b_ob_info, b_mesh, new_mesh.get_used_shaders(), dicing_rate, @@ -1191,15 +1199,15 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me else create_mesh(scene, &new_mesh, b_mesh, new_mesh.get_used_shaders(), false); - free_object_to_mesh(b_data, b_ob, b_mesh); + free_object_to_mesh(b_data, b_ob_info, b_mesh); } } /* cached velocities (e.g. from alembic archive) */ - sync_mesh_cached_velocities(b_ob, scene, &new_mesh); + sync_mesh_cached_velocities(b_ob_info, scene, &new_mesh); /* mesh fluid motion mantaflow */ - sync_mesh_fluid_motion(b_ob, scene, &new_mesh); + sync_mesh_fluid_motion(b_ob_info, scene, &new_mesh); /* update original sockets */ @@ -1230,18 +1238,19 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *me } void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, - BL::Object b_ob, + BObjectInfo &b_ob_info, Mesh *mesh, int motion_step) { /* Fluid motion blur already exported. */ - BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob); + BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob_info.real_object); if (b_fluid_domain) { return; } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find( + b_ob_info.real_object, true, nullptr); if (mesh_cache) { return; } @@ -1255,11 +1264,13 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, /* Skip objects without deforming modifiers. this is not totally reliable, * would need a more extensive check to see which objects are animated. */ BL::Mesh b_mesh(PointerRNA_NULL); - if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { + if (ccl::BKE_object_is_deform_modified(b_ob_info, b_scene, preview)) { /* get derived mesh */ - b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE); + b_mesh = object_to_mesh(b_data, b_ob_info, b_depsgraph, false, Mesh::SUBDIVISION_NONE); } + const std::string ob_name = b_ob_info.real_object.name(); + /* TODO(sergey): Perform preliminary check for number of vertices. */ if (b_mesh) { /* Export deformed coordinates. */ @@ -1295,17 +1306,17 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, memcmp(mP, &mesh->get_verts()[0], sizeof(float3) * numverts) == 0) { /* no motion, remove attributes again */ if (b_mesh.vertices.length() != numverts) { - VLOG(1) << "Topology differs, disabling motion blur for object " << b_ob.name(); + VLOG(1) << "Topology differs, disabling motion blur for object " << ob_name; } else { - VLOG(1) << "No actual deformation motion for object " << b_ob.name(); + VLOG(1) << "No actual deformation motion for object " << ob_name; } mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mN) mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL); } else if (motion_step > 0) { - VLOG(1) << "Filling deformation motion for object " << b_ob.name(); + VLOG(1) << "Filling deformation motion for object " << ob_name; /* motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now */ float3 *P = &mesh->get_verts()[0]; @@ -1319,8 +1330,8 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } else { if (b_mesh.vertices.length() != numverts) { - VLOG(1) << "Topology differs, discarding motion blur for object " << b_ob.name() - << " at time " << motion_step; + VLOG(1) << "Topology differs, discarding motion blur for object " << ob_name << " at time " + << motion_step; memcpy(mP, &mesh->get_verts()[0], sizeof(float3) * numverts); if (mN != NULL) { memcpy(mN, attr_N->data_float3(), sizeof(float3) * numverts); @@ -1328,7 +1339,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } } - free_object_to_mesh(b_data, b_ob, b_mesh); + free_object_to_mesh(b_data, b_ob_info, b_mesh); return; } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 5d98b61b409..2243baca0b2 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -154,7 +154,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, const bool is_instance = b_instance.is_instance(); BL::Object b_ob = b_instance.object(); BL::Object b_parent = is_instance ? b_instance.parent() : b_instance.object(); - BL::Object b_ob_instance = is_instance ? b_instance.instance_object() : b_ob; + BObjectInfo b_ob_info{b_ob, is_instance ? b_instance.instance_object() : b_ob, b_ob.data()}; const bool motion = motion_time != 0.0f; /*const*/ Transform tfm = get_transform(b_ob.matrix_world()); int *persistent_id = NULL; @@ -178,8 +178,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, { sync_light(b_parent, persistent_id, - b_ob, - b_ob_instance, + b_ob_info, is_instance ? b_instance.random_id() : 0, tfm, use_portal); @@ -231,7 +230,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, TaskPool *object_geom_task_pool = (is_instance) ? NULL : geom_task_pool; /* key to lookup object */ - ObjectKey key(b_parent, persistent_id, b_ob_instance, use_particle_hair); + ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, use_particle_hair); Object *object; /* motion vector case */ @@ -249,12 +248,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, /* mesh deformation */ if (object->get_geometry()) - sync_geometry_motion(b_depsgraph, - b_ob_instance, - object, - motion_time, - use_particle_hair, - object_geom_task_pool); + sync_geometry_motion( + b_depsgraph, b_ob_info, object, motion_time, use_particle_hair, object_geom_task_pool); } return object; @@ -265,15 +260,8 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, (tfm != object->get_tfm()); /* mesh sync */ - /* b_ob is owned by the iterator and will go out of scope at the end of the block. - * b_ob_instance is the original object and will remain valid for deferred geometry - * sync. */ - Geometry *geometry = sync_geometry(b_depsgraph, - b_ob_instance, - b_ob_instance, - object_updated, - use_particle_hair, - object_geom_task_pool); + Geometry *geometry = sync_geometry( + b_depsgraph, b_ob_info, object_updated, use_particle_hair, object_geom_task_pool); object->set_geometry(geometry); /* special case not tracked by object update flags */ diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 44322dda6b9..76e8f23864c 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -23,6 +23,7 @@ #include "RNA_types.h" #include "blender/blender_id_map.h" +#include "blender/blender_util.h" #include "blender/blender_viewport.h" #include "render/scene.h" @@ -158,18 +159,24 @@ class BlenderSync { bool sync_object_attributes(BL::DepsgraphObjectInstance &b_instance, Object *object); /* Volume */ - void sync_volume(BL::Object &b_ob, Volume *volume); + void sync_volume(BObjectInfo &b_ob_info, Volume *volume); /* Mesh */ - void sync_mesh(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh); - void sync_mesh_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Mesh *mesh, int motion_step); + void sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh); + void sync_mesh_motion(BL::Depsgraph b_depsgraph, + BObjectInfo &b_ob_info, + Mesh *mesh, + int motion_step); /* Hair */ - void sync_hair(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair); - void sync_hair_motion(BL::Depsgraph b_depsgraph, BL::Object b_ob, Hair *hair, int motion_step); - void sync_hair(Hair *hair, BL::Object &b_ob, bool motion, int motion_step = 0); + void sync_hair(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Hair *hair); + void sync_hair_motion(BL::Depsgraph b_depsgraph, + BObjectInfo &b_ob_info, + Hair *hair, + int motion_step); + void sync_hair(Hair *hair, BObjectInfo &b_ob_info, bool motion, int motion_step = 0); void sync_particle_hair( - Hair *hair, BL::Mesh &b_mesh, BL::Object &b_ob, bool motion, int motion_step = 0); + Hair *hair, BL::Mesh &b_mesh, BObjectInfo &b_ob_info, bool motion, int motion_step = 0); bool object_has_particle_hair(BL::Object b_ob); /* Camera */ @@ -178,14 +185,13 @@ class BlenderSync { /* Geometry */ Geometry *sync_geometry(BL::Depsgraph &b_depsgrpah, - BL::Object &b_ob, - BL::Object &b_ob_instance, + BObjectInfo &b_ob_info, bool object_updated, bool use_particle_hair, TaskPool *task_pool); void sync_geometry_motion(BL::Depsgraph &b_depsgraph, - BL::Object &b_ob, + BObjectInfo &b_ob_info, Object *object, float motion_time, bool use_particle_hair, @@ -194,8 +200,7 @@ class BlenderSync { /* Light */ void sync_light(BL::Object &b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], - BL::Object &b_ob, - BL::Object &b_ob_instance, + BObjectInfo &b_ob_info, int random_id, Transform &tfm, bool *use_portal); diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index f6824f31b7b..e69531ea707 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -40,6 +40,28 @@ float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile); CCL_NAMESPACE_BEGIN +struct BObjectInfo { + /* Object directly provided by the depsgraph iterator. This object is only valid during one + * iteration and must not be accessed afterwards. Transforms and visibility should be checked on + * this object. */ + BL::Object iter_object; + + /* This object remains alive even after the object iterator is done. It corresponds to one + * original object. It is the object that owns the object data below. */ + BL::Object real_object; + + /* The object-data referenced by the iter object. This is still valid after the depsgraph + * iterator is done. It might have a different type compared to real_object.data(). */ + BL::ID object_data; + + /* True when the current geometry is the data of the referenced object. False when it is a + * geometry instance that does not have a 1-to-1 relationship with an object. */ + bool is_real_object_data() const + { + return const_cast(real_object).data() == object_data; + } +}; + typedef BL::ShaderNodeAttribute::attribute_type_enum BlenderAttributeType; BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_real_name); @@ -47,7 +69,7 @@ void python_thread_state_save(void **python_thread_state); void python_thread_state_restore(void **python_thread_state); static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, - BL::Object &object, + BObjectInfo &b_ob_info, BL::Depsgraph & /*depsgraph*/, bool /*calc_undeformed*/, Mesh::SubdivisionType subdivision_type) @@ -69,9 +91,9 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, #endif BL::Mesh mesh(PointerRNA_NULL); - if (object.type() == BL::Object::type_MESH) { + if (b_ob_info.object_data.is_a(&RNA_Mesh)) { /* TODO: calc_undeformed is not used. */ - mesh = BL::Mesh(object.data()); + mesh = BL::Mesh(b_ob_info.object_data); /* Make a copy to split faces if we use autosmooth, otherwise not needed. * Also in edit mode do we need to make a copy, to ensure data layers like @@ -79,12 +101,15 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, if (mesh.is_editmode() || (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { BL::Depsgraph depsgraph(PointerRNA_NULL); - mesh = object.to_mesh(false, depsgraph); + assert(b_ob_info.is_real_object_data()); + mesh = b_ob_info.real_object.to_mesh(false, depsgraph); } } else { BL::Depsgraph depsgraph(PointerRNA_NULL); - mesh = object.to_mesh(false, depsgraph); + if (b_ob_info.is_real_object_data()) { + mesh = b_ob_info.real_object.to_mesh(false, depsgraph); + } } #if 0 @@ -108,10 +133,14 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, } static inline void free_object_to_mesh(BL::BlendData & /*data*/, - BL::Object &object, + BObjectInfo &b_ob_info, BL::Mesh &mesh) { + if (!b_ob_info.is_real_object_data()) { + return; + } /* Free mesh if we didn't just use the existing one. */ + BL::Object object = b_ob_info.real_object; if (object.data().ptr.data != mesh.ptr.data) { object.to_mesh_clear(); } @@ -219,9 +248,13 @@ static inline bool BKE_object_is_modified(BL::Object &self, BL::Scene &scene, bo return self.is_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false; } -static inline bool BKE_object_is_deform_modified(BL::Object &self, BL::Scene &scene, bool preview) +static inline bool BKE_object_is_deform_modified(BObjectInfo &self, BL::Scene &scene, bool preview) { - return self.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : false; + if (!self.is_real_object_data()) { + return false; + } + return self.real_object.is_deform_modified(scene, (preview) ? (1 << 0) : (1 << 1)) ? true : + false; } static inline int render_resolution_x(BL::RenderSettings &b_render) diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp index 772ab9f5c8a..0a5b19d7d4c 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/blender_volume.cpp @@ -181,9 +181,12 @@ class BlenderSmokeLoader : public ImageLoader { AttributeStandard attribute; }; -static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, float frame) +static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame) { - BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob); + if (!b_ob_info.is_real_object_data()) { + return; + } + BL::FluidDomainSettings b_domain = object_fluid_gas_domain_find(b_ob_info.real_object); if (!b_domain) { return; } @@ -206,7 +209,7 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Volume *volume, fl Attribute *attr = volume->attributes.add(std); - ImageLoader *loader = new BlenderSmokeLoader(b_ob, std); + ImageLoader *loader = new BlenderSmokeLoader(b_ob_info.real_object, std); ImageParams params; params.frame = frame; @@ -244,11 +247,11 @@ class BlenderVolumeLoader : public VDBImageLoader { }; static void sync_volume_object(BL::BlendData &b_data, - BL::Object &b_ob, + BObjectInfo &b_ob_info, Scene *scene, Volume *volume) { - BL::Volume b_volume(b_ob.data()); + BL::Volume b_volume(b_ob_info.object_data); b_volume.grids.load(b_data.ptr.data); BL::VolumeRender b_render(b_volume.render()); @@ -296,19 +299,19 @@ static void sync_volume_object(BL::BlendData &b_data, } } -void BlenderSync::sync_volume(BL::Object &b_ob, Volume *volume) +void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume) { volume->clear(true); if (view_layer.use_volumes) { - if (b_ob.type() == BL::Object::type_VOLUME) { + if (b_ob_info.object_data.is_a(&RNA_Volume)) { /* Volume object. Create only attributes, bounding mesh will then * be automatically generated later. */ - sync_volume_object(b_data, b_ob, scene, volume); + sync_volume_object(b_data, b_ob_info, scene, volume); } else { /* Smoke domain. */ - sync_smoke_volume(scene, b_ob, volume, b_scene.frame_current()); + sync_smoke_volume(scene, b_ob_info, volume, b_scene.frame_current()); } } diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h index c142d5338d1..989b68f4ccb 100644 --- a/source/blender/blenkernel/BKE_duplilist.h +++ b/source/blender/blenkernel/BKE_duplilist.h @@ -31,6 +31,7 @@ struct ListBase; struct Object; struct ParticleSystem; struct Scene; +struct ID; /* ---------------------------------------------------- */ /* Dupli-Geometry */ @@ -42,7 +43,10 @@ void free_object_duplilist(struct ListBase *lb); typedef struct DupliObject { struct DupliObject *next, *prev; + /* Object whose geometry is instanced. */ struct Object *ob; + /* Data owned by the object above that is instanced. This might not be the same as `ob->data`. */ + struct ID *ob_data; float mat[4][4]; float orco[3], uv[2]; diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h index 5f6a9ec7b91..17cdb9d6a42 100644 --- a/source/blender/blenkernel/BKE_geometry_set.h +++ b/source/blender/blenkernel/BKE_geometry_set.h @@ -41,7 +41,7 @@ typedef enum GeometryComponentType { void BKE_geometry_set_free(struct GeometrySet *geometry_set); -bool BKE_geometry_set_has_instances(const struct GeometrySet *geometry_set); +bool BKE_object_has_geometry_set_instances(const struct Object *ob); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 42e9ce82278..08b6cb951a8 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -283,6 +283,7 @@ struct GeometrySet { void clear(); + bool owns_direct_data() const; void ensure_owns_direct_data(); /* Utility methods for creation. */ @@ -447,12 +448,14 @@ class InstanceReference { None, Object, Collection, + GeometrySet, }; private: Type type_ = Type::None; /** Depending on the type this is either null, an Object or Collection pointer. */ void *data_ = nullptr; + std::unique_ptr geometry_set_; public: InstanceReference() = default; @@ -465,6 +468,19 @@ class InstanceReference { { } + InstanceReference(GeometrySet geometry_set) + : type_(Type::GeometrySet), + geometry_set_(std::make_unique(std::move(geometry_set))) + { + } + + InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_) + { + if (src.type_ == Type::GeometrySet) { + geometry_set_ = std::make_unique(*src.geometry_set_); + } + } + Type type() const { return type_; @@ -482,14 +498,37 @@ class InstanceReference { return *(Collection *)data_; } + const GeometrySet &geometry_set() const + { + BLI_assert(type_ == Type::GeometrySet); + return *geometry_set_; + } + + bool owns_direct_data() const + { + if (type_ != Type::GeometrySet) { + /* The object and collection instances are not direct data. */ + return true; + } + return geometry_set_->owns_direct_data(); + } + + void ensure_owns_direct_data() + { + if (type_ != Type::GeometrySet) { + return; + } + geometry_set_->ensure_owns_direct_data(); + } + uint64_t hash() const { - return blender::get_default_hash(data_); + return blender::get_default_hash_2(data_, geometry_set_.get()); } friend bool operator==(const InstanceReference &a, const InstanceReference &b) { - return a.data_ == b.data_; + return a.data_ == b.data_ && a.geometry_set_.get() == b.geometry_set_.get(); } }; @@ -529,7 +568,7 @@ class InstancesComponent : public GeometryComponent { void reserve(int min_capacity); void resize(int capacity); - int add_reference(InstanceReference reference); + int add_reference(const InstanceReference &reference); void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); blender::Span references() const; diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index a823602e341..31b3cd66cbb 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -458,6 +458,8 @@ void BKE_object_modifiers_lib_link_common(void *userData, struct ID **idpoin, int cb_flag); +void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_data); + struct PartEff; struct PartEff *BKE_object_do_version_give_parteff_245(struct Object *ob); diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 3b1b7456162..26ef827d36d 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -122,7 +122,7 @@ blender::Span InstancesComponent::instance_ids() const * If the reference exists already, the handle of the existing reference is returned. * Otherwise a new handle is added. */ -int InstancesComponent::add_reference(InstanceReference reference) +int InstancesComponent::add_reference(const InstanceReference &reference) { return references_.index_of_or_add_as(reference); } @@ -144,14 +144,23 @@ bool InstancesComponent::is_empty() const bool InstancesComponent::owns_direct_data() const { - /* The object and collection instances are not direct data. Instance transforms are direct data - * and are always owned. Therefore, instance components always own all their direct data. */ + for (const InstanceReference &reference : references_) { + if (!reference.owns_direct_data()) { + return false; + } + } return true; } void InstancesComponent::ensure_owns_direct_data() { BLI_assert(this->is_mutable()); + for (const InstanceReference &const_reference : references_) { + /* Const cast is fine because we are not changing anything that would change the hash of the + * reference. */ + InstanceReference &reference = const_cast(const_reference); + reference.ensure_owns_direct_data(); + } } static blender::Array generate_unique_instance_ids(Span original_ids) diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 07b4e715ea9..dafebef1812 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -218,6 +218,16 @@ void GeometrySet::ensure_owns_direct_data() } } +bool GeometrySet::owns_direct_data() const +{ + for (const GeometryComponentPtr &component : components_.values()) { + if (!component->owns_direct_data()) { + return false; + } + } + return true; +} + /* Returns a read-only mesh or null. */ const Mesh *GeometrySet::get_mesh_for_read() const { @@ -376,9 +386,32 @@ void BKE_geometry_set_free(GeometrySet *geometry_set) delete geometry_set; } -bool BKE_geometry_set_has_instances(const GeometrySet *geometry_set) +bool BKE_object_has_geometry_set_instances(const Object *ob) { - return geometry_set->get_component_for_read() != nullptr; + const GeometrySet *geometry_set = ob->runtime.geometry_set_eval; + if (geometry_set == nullptr) { + return false; + } + if (geometry_set->has_instances()) { + return true; + } + const bool has_mesh = geometry_set->has_mesh(); + const bool has_pointcloud = geometry_set->has_pointcloud(); + const bool has_volume = geometry_set->has_volume(); + const bool has_curve = geometry_set->has_curve(); + if (ob->type == OB_MESH) { + return has_pointcloud || has_volume || has_curve; + } + if (ob->type == OB_POINTCLOUD) { + return has_mesh || has_volume || has_curve; + } + if (ob->type == OB_VOLUME) { + return has_mesh || has_pointcloud || has_curve; + } + if (ob->type == OB_CURVE) { + return has_mesh || has_pointcloud || has_volume; + } + return false; } /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 32a65ab47bf..376792b9b96 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -168,6 +168,11 @@ static void geometry_set_collect_recursive(const GeometrySet &geometry_set, collection, instance_transform, r_sets); break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + geometry_set_collect_recursive(geometry_set, instance_transform, r_sets); + break; + } case InstanceReference::Type::None: { break; } @@ -290,6 +295,13 @@ static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_se } break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) { + return false; + } + break; + } case InstanceReference::Type::None: { break; } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 062264c5729..bca1afbf101 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1979,8 +1979,7 @@ int BKE_object_visibility(const Object *ob, const int dag_eval_mode) visibility |= OB_VISIBLE_INSTANCES; } - if (ob->runtime.geometry_set_eval != NULL && - BKE_geometry_set_has_instances(ob->runtime.geometry_set_eval)) { + if (BKE_object_has_geometry_set_instances(ob)) { visibility |= OB_VISIBLE_INSTANCES; } @@ -5737,3 +5736,13 @@ void BKE_object_modifiers_lib_link_common(void *userData, id_us_plus_no_lib(*idpoin); } } + +void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) +{ + ob->type = BKE_object_obdata_to_type(new_data); + ob->data = new_data; + ob->runtime.geometry_set_eval = NULL; + ob->runtime.data_eval = NULL; + ob->runtime.bb->flag |= BOUNDBOX_DIRTY; + ob->id.py_instance = NULL; +} diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index a46ac4b1175..47d4d03d1e1 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -194,6 +194,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, } dob->ob = ob; + dob->ob_data = (ID *)ob->data; mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); dob->type = ctx->gen->type; @@ -834,14 +835,59 @@ static const DupliGenerator gen_dupli_verts_pointcloud = { /** \name Instances Geometry Component Implementation * \{ */ -static void make_duplis_instances_component(const DupliContext *ctx) +static void make_duplis_geometry_set_impl(const DupliContext *ctx, + const GeometrySet &geometry_set, + const float parent_transform[4][4], + bool geometry_set_is_instance) { - const InstancesComponent *component = - ctx->object->runtime.geometry_set_eval->get_component_for_read(); + int component_index = 0; + if (ctx->object->type != OB_MESH || geometry_set_is_instance) { + const Mesh *mesh = geometry_set.get_mesh_for_read(); + if (mesh != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)mesh; + } + } + if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) { + const Volume *volume = geometry_set.get_volume_for_read(); + if (volume != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)volume; + } + } + if (ctx->object->type != OB_CURVE || geometry_set_is_instance) { + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + if (curve_component != nullptr) { + const Curve *curve = curve_component->get_curve_for_render(); + if (curve != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)curve; + } + } + } + if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) { + const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read(); + if (pointcloud != nullptr) { + DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); + dupli->ob_data = (ID *)pointcloud; + } + } + const bool creates_duplis_for_components = component_index >= 1; + + const InstancesComponent *component = geometry_set.get_component_for_read(); if (component == nullptr) { return; } + const DupliContext *instances_ctx = ctx; + /* Create a sub-context if some duplis were created above. This is to avoid dupli id collisions + * between the instances component below and the other components above. */ + DupliContext new_instances_ctx; + if (creates_duplis_for_components) { + copy_dupli_context(&new_instances_ctx, ctx, ctx->object, nullptr, component_index); + instances_ctx = &new_instances_ctx; + } + Span instance_offset_matrices = component->instance_transforms(); Span instance_reference_handles = component->instance_reference_handles(); Span almost_unique_ids = component->almost_unique_ids(); @@ -855,13 +901,13 @@ static void make_duplis_instances_component(const DupliContext *ctx) case InstanceReference::Type::Object: { Object &object = reference.object(); float matrix[4][4]; - mul_m4_m4m4(matrix, ctx->object->obmat, instance_offset_matrices[i].values); - make_dupli(ctx, &object, matrix, id); + mul_m4_m4m4(matrix, parent_transform, instance_offset_matrices[i].values); + make_dupli(instances_ctx, &object, matrix, id); float space_matrix[4][4]; mul_m4_m4m4(space_matrix, instance_offset_matrices[i].values, object.imat); - mul_m4_m4_pre(space_matrix, ctx->object->obmat); - make_recursive_duplis(ctx, &object, space_matrix, id); + mul_m4_m4_pre(space_matrix, parent_transform); + make_recursive_duplis(instances_ctx, &object, space_matrix, id); break; } case InstanceReference::Type::Collection: { @@ -870,23 +916,36 @@ static void make_duplis_instances_component(const DupliContext *ctx) unit_m4(collection_matrix); sub_v3_v3(collection_matrix[3], collection.instance_offset); mul_m4_m4_pre(collection_matrix, instance_offset_matrices[i].values); - mul_m4_m4_pre(collection_matrix, ctx->object->obmat); + mul_m4_m4_pre(collection_matrix, parent_transform); + + DupliContext sub_ctx; + copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id); - eEvaluationMode mode = DEG_get_mode(ctx->depsgraph); + eEvaluationMode mode = DEG_get_mode(instances_ctx->depsgraph); + int object_id = 0; FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (&collection, object, mode) { - if (object == ctx->object) { + if (object == instances_ctx->object) { continue; } float instance_matrix[4][4]; mul_m4_m4m4(instance_matrix, collection_matrix, object->obmat); - make_dupli(ctx, object, instance_matrix, id); - make_recursive_duplis(ctx, object, collection_matrix, id); + make_dupli(&sub_ctx, object, instance_matrix, object_id++); + make_recursive_duplis(&sub_ctx, object, collection_matrix, object_id++); } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; break; } + case InstanceReference::Type::GeometrySet: { + float new_transform[4][4]; + mul_m4_m4m4(new_transform, parent_transform, instance_offset_matrices[i].values); + + DupliContext sub_ctx; + copy_dupli_context(&sub_ctx, instances_ctx, instances_ctx->object, nullptr, id); + make_duplis_geometry_set_impl(&sub_ctx, reference.geometry_set(), new_transform, true); + break; + } case InstanceReference::Type::None: { break; } @@ -894,9 +953,15 @@ static void make_duplis_instances_component(const DupliContext *ctx) } } -static const DupliGenerator gen_dupli_instances_component = { +static void make_duplis_geometry_set(const DupliContext *ctx) +{ + const GeometrySet *geometry_set = ctx->object->runtime.geometry_set_eval; + make_duplis_geometry_set_impl(ctx, *geometry_set, ctx->object->obmat, false); +} + +static const DupliGenerator gen_dupli_geometry_set = { 0, - make_duplis_instances_component, + make_duplis_geometry_set, }; /** \} */ @@ -1567,8 +1632,8 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx) } if (ctx->object->runtime.geometry_set_eval != nullptr) { - if (BKE_geometry_set_has_instances(ctx->object->runtime.geometry_set_eval)) { - return &gen_dupli_instances_component; + if (BKE_object_has_geometry_set_instances(ctx->object)) { + return &gen_dupli_geometry_set; } } diff --git a/source/blender/blenlib/BLI_user_counter.hh b/source/blender/blenlib/BLI_user_counter.hh index 3e6d5af4c3f..8cebadeac4c 100644 --- a/source/blender/blenlib/BLI_user_counter.hh +++ b/source/blender/blenlib/BLI_user_counter.hh @@ -84,12 +84,24 @@ template class UserCounter { return data_; } + const T *operator->() const + { + BLI_assert(data_ != nullptr); + return data_; + } + T &operator*() { BLI_assert(data_ != nullptr); return *data_; } + const T &operator*() const + { + BLI_assert(data_ != nullptr); + return *data_; + } + operator bool() const { return data_ != nullptr; diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 17f5ca0db79..e9195a1eb26 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -145,15 +145,7 @@ typedef struct DEGObjectIterData { eEvaluationMode eval_mode; - /* **** Iteration over geometry components **** */ - - /* The object whose components we currently iterate over. - * This might point to #temp_dupli_object. */ - struct Object *geometry_component_owner; - /* Some identifier that is used to determine which geometry component should be returned next. */ - int geometry_component_id; - /* Temporary storage for an object that is created from a component. */ - struct Object temp_geometry_component_object; + struct Object *next_object; /* **** Iteration over dupli-list. *** */ diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc index 770d9775dd3..7af3d03d478 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc @@ -120,130 +120,6 @@ bool deg_object_hide_original(eEvaluationMode eval_mode, Object *ob, DupliObject return false; } -void deg_iterator_components_init(DEGObjectIterData *data, Object *object) -{ - data->geometry_component_owner = object; - data->geometry_component_id = 0; -} - -/* Returns false when iterator is exhausted. */ -bool deg_iterator_components_step(BLI_Iterator *iter) -{ - DEGObjectIterData *data = (DEGObjectIterData *)iter->data; - if (data->geometry_component_owner == nullptr) { - return false; - } - - if (data->geometry_component_owner->runtime.geometry_set_eval == nullptr) { - /* Return the object itself, if it does not have a geometry set yet. */ - iter->current = data->geometry_component_owner; - data->geometry_component_owner = nullptr; - return true; - } - - GeometrySet *geometry_set = data->geometry_component_owner->runtime.geometry_set_eval; - if (geometry_set == nullptr) { - data->geometry_component_owner = nullptr; - return false; - } - - /* The mesh component. */ - if (data->geometry_component_id == 0) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a mesh object. */ - if (data->geometry_component_owner->type == OB_MESH) { - iter->current = data->geometry_component_owner; - return true; - } - - const Mesh *mesh = geometry_set->get_mesh_for_read(); - if (mesh != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_MESH; - temp_object->data = (void *)mesh; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - - /* The pointcloud component. */ - if (data->geometry_component_id == 1) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a point cloud object. */ - if (data->geometry_component_owner->type == OB_POINTCLOUD) { - iter->current = data->geometry_component_owner; - return true; - } - - const PointCloud *pointcloud = geometry_set->get_pointcloud_for_read(); - if (pointcloud != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_POINTCLOUD; - temp_object->data = (void *)pointcloud; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - - /* The volume component. */ - if (data->geometry_component_id == 2) { - data->geometry_component_id++; - - /* Don't use a temporary object for this component, when the owner is a volume object. */ - if (data->geometry_component_owner->type == OB_VOLUME) { - iter->current = data->geometry_component_owner; - return true; - } - - const VolumeComponent *component = geometry_set->get_component_for_read(); - if (component != nullptr) { - const Volume *volume = component->get_for_read(); - - if (volume != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_VOLUME; - temp_object->data = (void *)volume; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - } - - /* The curve component. */ - if (data->geometry_component_id == 3) { - data->geometry_component_id++; - - const CurveComponent *component = geometry_set->get_component_for_read(); - if (component != nullptr) { - const Curve *curve = component->get_curve_for_render(); - - if (curve != nullptr) { - Object *temp_object = &data->temp_geometry_component_object; - *temp_object = *data->geometry_component_owner; - temp_object->type = OB_CURVE; - temp_object->data = (void *)curve; - /* Assign data_eval here too, because curve rendering code tries - * to use a mesh if it can find one in this pointer. */ - temp_object->runtime.data_eval = (ID *)curve; - temp_object->runtime.select_id = data->geometry_component_owner->runtime.select_id; - iter->current = temp_object; - return true; - } - } - } - - data->geometry_component_owner = nullptr; - return false; -} - void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object) { if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) && @@ -292,6 +168,9 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data) temp_dupli_object->dt = MIN2(temp_dupli_object->dt, dupli_parent->dt); copy_v4_v4(temp_dupli_object->color, dupli_parent->color); temp_dupli_object->runtime.select_id = dupli_parent->runtime.select_id; + if (dob->ob->data != dob->ob_data) { + BKE_object_replace_data_on_shallow_copy(temp_dupli_object, dob->ob_data); + } /* Duplicated elements shouldn't care whether their original collection is visible or not. */ temp_dupli_object->base_flag |= BASE_VISIBLE_DEPSGRAPH; @@ -308,7 +187,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data) copy_m4_m4(data->temp_dupli_object.obmat, dob->mat); invert_m4_m4(data->temp_dupli_object.imat, data->temp_dupli_object.obmat); - deg_iterator_components_init(data, &data->temp_dupli_object); + data->next_object = &data->temp_dupli_object; BLI_assert(deg::deg_validate_copy_on_write_datablock(&data->temp_dupli_object.id)); return true; } @@ -377,7 +256,7 @@ bool deg_iterator_objects_step(DEGObjectIterData *data) } if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) { - deg_iterator_components_init(data, object); + data->next_object = object; } data->id_node_index++; return true; @@ -400,6 +279,7 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) return; } + data->next_object = nullptr; data->dupli_parent = nullptr; data->dupli_list = nullptr; data->dupli_object_next = nullptr; @@ -408,8 +288,6 @@ void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data) data->id_node_index = 0; data->num_id_nodes = num_id_nodes; data->eval_mode = DEG_get_mode(depsgraph); - data->geometry_component_id = 0; - data->geometry_component_owner = nullptr; deg_invalidate_iterator_work_data(data); DEG_iterator_objects_next(iter); @@ -419,7 +297,9 @@ void DEG_iterator_objects_next(BLI_Iterator *iter) { DEGObjectIterData *data = (DEGObjectIterData *)iter->data; while (true) { - if (deg_iterator_components_step(iter)) { + if (data->next_object != nullptr) { + iter->current = data->next_object; + data->next_object = nullptr; return; } if (deg_iterator_duplis_step(data)) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 7f850435a64..9590a4aa7ee 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -632,14 +632,29 @@ void DRW_viewport_request_redraw(void) /** \name Duplis * \{ */ -static void drw_duplidata_load(DupliObject *dupli) +static uint dupli_key_hash(const void *key) { + const DupliKey *dupli_key = (const DupliKey *)key; + return BLI_ghashutil_ptrhash(dupli_key->ob) ^ BLI_ghashutil_ptrhash(dupli_key->ob_data); +} + +static bool dupli_key_cmp(const void *key1, const void *key2) +{ + const DupliKey *dupli_key1 = (const DupliKey *)key1; + const DupliKey *dupli_key2 = (const DupliKey *)key2; + return dupli_key1->ob != dupli_key2->ob || dupli_key1->ob_data != dupli_key2->ob_data; +} + +static void drw_duplidata_load(Object *ob) +{ + DupliObject *dupli = DST.dupli_source; if (dupli == NULL) { return; } - if (DST.dupli_origin != dupli->ob) { + if (DST.dupli_origin != dupli->ob || (DST.dupli_origin_data != dupli->ob_data)) { DST.dupli_origin = dupli->ob; + DST.dupli_origin_data = dupli->ob_data; } else { /* Same data as previous iter. No need to poll ghash for this. */ @@ -647,16 +662,23 @@ static void drw_duplidata_load(DupliObject *dupli) } if (DST.dupli_ghash == NULL) { - DST.dupli_ghash = BLI_ghash_ptr_new(__func__); + DST.dupli_ghash = BLI_ghash_new(dupli_key_hash, dupli_key_cmp, __func__); } + DupliKey *key = MEM_callocN(sizeof(DupliKey), __func__); + key->ob = dupli->ob; + key->ob_data = dupli->ob_data; + void **value; - if (!BLI_ghash_ensure_p(DST.dupli_ghash, DST.dupli_origin, &value)) { + if (!BLI_ghash_ensure_p(DST.dupli_ghash, key, &value)) { *value = MEM_callocN(sizeof(void *) * DST.enabled_engine_count, __func__); /* TODO: Meh a bit out of place but this is nice as it is - * only done once per "original" object. */ - drw_batch_cache_validate(DST.dupli_origin); + * only done once per instance type. */ + drw_batch_cache_validate(ob); + } + else { + MEM_freeN(key); } DST.dupli_datas = *(void ***)value; } @@ -670,12 +692,24 @@ static void duplidata_value_free(void *val) MEM_freeN(val); } +static void duplidata_key_free(void *key) +{ + DupliKey *dupli_key = (DupliKey *)key; + if (dupli_key->ob_data == NULL) { + drw_batch_cache_generate_requested(dupli_key->ob); + } + else { + Object temp_object = *dupli_key->ob; + BKE_object_replace_data_on_shallow_copy(&temp_object, dupli_key->ob_data); + drw_batch_cache_generate_requested(&temp_object); + } + MEM_freeN(key); +} + static void drw_duplidata_free(void) { if (DST.dupli_ghash != NULL) { - BLI_ghash_free(DST.dupli_ghash, - (void (*)(void *key))drw_batch_cache_generate_requested, - duplidata_value_free); + BLI_ghash_free(DST.dupli_ghash, duplidata_key_free, duplidata_value_free); DST.dupli_ghash = NULL; } } @@ -1523,6 +1557,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, /* Only iterate over objects for internal engines or when overlays are enabled */ if (do_populate_loop) { DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; @@ -1532,7 +1567,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, } DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; @@ -1881,12 +1916,13 @@ void DRW_render_object_iter( draw_ctx->v3d->object_type_exclude_viewport : 0; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) == 0) { DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; DST.ob_handle = 0; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); if (!DST.dupli_source) { drw_batch_cache_validate(ob); @@ -2330,6 +2366,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, v3d->object_type_exclude_select); bool filter_exclude = false; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (depsgraph, ob) { if (!BKE_object_is_visible_in_viewport(v3d, ob)) { continue; @@ -2362,7 +2399,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_select_load_id(ob->runtime.select_id); DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } } @@ -2475,6 +2512,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, const int object_type_exclude_viewport = v3d->object_type_exclude_viewport; DST.dupli_origin = NULL; + DST.dupli_origin_data = NULL; DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN (DST.draw_ctx.depsgraph, ob) { if ((object_type_exclude_viewport & (1 << ob->type)) != 0) { continue; @@ -2484,7 +2522,7 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, } DST.dupli_parent = data_.dupli_parent; DST.dupli_source = data_.dupli_object_current; - drw_duplidata_load(DST.dupli_source); + drw_duplidata_load(ob); drw_engines_cache_populate(ob); } DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 33e1a57198c..1747ca752c7 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -497,6 +497,11 @@ typedef struct DRWDebugSphere { /* ------------- DRAW MANAGER ------------ */ +typedef struct DupliKey { + struct Object *ob; + struct ID *ob_data; +} DupliKey; + #define DST_MAX_SLOTS 64 /* Cannot be changed without modifying RST.bound_tex_slots */ #define MAX_CLIP_PLANES 6 /* GL_MAX_CLIP_PLANES is at least 6 */ #define STENCIL_UNDEFINED 256 @@ -515,15 +520,19 @@ typedef struct DRWManager { /** Handle of next DRWPass to be allocated. */ DRWResourceHandle pass_handle; - /** Dupli state. NULL if not dupli. */ + /** Dupli object that corresponds to the current object. */ struct DupliObject *dupli_source; + /** Object that created the dupli-list the current object is part of. */ struct Object *dupli_parent; + /** Object referenced by the current dupli object. */ struct Object *dupli_origin; - /** Ghash containing original objects. */ + /** Object-data referenced by the current dupli object. */ + struct ID *dupli_origin_data; + /** Ghash: #DupliKey -> void pointer for each enabled engine. */ struct GHash *dupli_ghash; /** TODO(fclem): try to remove usage of this. */ DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE]; - /* Array of dupli_data (one for each enabled engine) to handle duplis. */ + /* Dupli data for the current dupli for each enabled engine. */ void **dupli_datas; /* Rendering state */ diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 2e34284f46e..f95e3f3b236 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2246,13 +2246,6 @@ static bool dupliobject_instancer_cmp(const void *a_, const void *b_) return false; } -static bool object_has_geometry_set_instances(const Object *object_eval) -{ - struct GeometrySet *geometry_set = object_eval->runtime.geometry_set_eval; - - return (geometry_set != NULL) && BKE_geometry_set_has_instances(geometry_set); -} - static void make_object_duplilist_real(bContext *C, Depsgraph *depsgraph, Scene *scene, @@ -2266,7 +2259,8 @@ static void make_object_duplilist_real(bContext *C, Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object); - if (!(base->object->transflag & OB_DUPLI) && !object_has_geometry_set_instances(object_eval)) { + if (!(base->object->transflag & OB_DUPLI) && + !BKE_object_has_geometry_set_instances(object_eval)) { return; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 680da9b6794..97170693cb3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -35,6 +35,10 @@ struct CollectionCellValue { const Collection *collection; }; +struct GeometrySetCellValue { + const GeometrySet *geometry_set; +}; + /** * This is a type that can hold the value of a cell in a spreadsheet. This type allows us to * decouple the drawing of individual cells from the code that generates the data to be displayed. @@ -53,6 +57,7 @@ class CellValue { std::optional value_color; std::optional value_object; std::optional value_collection; + std::optional value_geometry_set; }; } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index e38c70afd0f..12815c2c7e9 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -332,6 +332,11 @@ std::unique_ptr InstancesDataSource::get_column_values( r_cell_value.value_collection = CollectionCellValue{&collection}; break; } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &geometry_set = reference.geometry_set(); + r_cell_value.value_geometry_set = GeometrySetCellValue{&geometry_set}; + break; + } case InstanceReference::Type::None: { break; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 8079763a339..1a5eac53306 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -209,6 +209,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { 0, nullptr); } + else if (cell_value.value_geometry_set.has_value()) { + uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_MESH_DATA, + "Geometry", + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } } void draw_float_vector(const CellDrawParams ¶ms, const Span values) const diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index bb04f557074..811f30c96e5 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -501,9 +501,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, } Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object); - if (obj_eval->transflag & OB_DUPLI || - (obj_eval->runtime.geometry_set_eval != NULL && - BKE_geometry_set_has_instances(obj_eval->runtime.geometry_set_eval))) { + if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval); for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { BLI_assert(DEG_is_evaluated_object(dupli_ob->ob)); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 43dd4b4270e..fd67150811d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1889,6 +1889,7 @@ typedef enum GeometryNodeTriangulateQuads { typedef enum GeometryNodePointInstanceType { GEO_NODE_POINT_INSTANCE_TYPE_OBJECT = 0, GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION = 1, + GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY = 2, } GeometryNodePointInstanceType; typedef enum GeometryNodePointInstanceFlag { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 9e24a36915e..a7c0372f309 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9295,6 +9295,11 @@ static void def_geo_point_instance(StructRNA *srna) ICON_NONE, "Collection", "Instance an entire collection on all points"}, + {GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY, + "GEOMETRY", + ICON_NONE, + "Geometry", + "Copy geometry to all points"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 36017307739..9f67638de7e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -31,13 +31,14 @@ static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) b.add_input("Geometry"); b.add_input("Object").hide_label(true); b.add_input("Collection").hide_label(true); + b.add_input("Instance Geometry"); b.add_input("Seed").min(-10000).max(10000); b.add_output("Geometry"); } static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "instance_type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "instance_type", 0, nullptr, ICON_NONE); if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { uiItemR(layout, ptr, "use_whole_collection", 0, nullptr, ICON_NONE); } @@ -56,7 +57,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) { bNodeSocket *object_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); bNodeSocket *collection_socket = object_socket->next; - bNodeSocket *seed_socket = collection_socket->next; + bNodeSocket *instance_geometry_socket = collection_socket->next; + bNodeSocket *seed_socket = instance_geometry_socket->next; NodeGeometryPointInstance *node_storage = (NodeGeometryPointInstance *)node->storage; GeometryNodePointInstanceType type = (GeometryNodePointInstanceType)node_storage->instance_type; @@ -65,6 +67,8 @@ static void geo_node_point_instance_update(bNodeTree *UNUSED(tree), bNode *node) nodeSetSocketAvailability(object_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_OBJECT); nodeSetSocketAvailability(collection_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION); + nodeSetSocketAvailability(instance_geometry_socket, + type == GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY); nodeSetSocketAvailability( seed_socket, type == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION && !use_whole_collection); } @@ -114,6 +118,12 @@ static Vector get_instance_references__collection(GeoNodeExec return references; } +static Vector get_instance_references__geometry(GeoNodeExecParams ¶ms) +{ + GeometrySet geometry_set = params.extract_input("Instance Geometry"); + return {std::move(geometry_set)}; +} + static Vector get_instance_references(GeoNodeExecParams ¶ms) { const bNode &node = params.node(); @@ -128,6 +138,9 @@ static Vector get_instance_references(GeoNodeExecParams ¶ case GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION: { return get_instance_references__collection(params); } + case GEO_NODE_POINT_INSTANCE_TYPE_GEOMETRY: { + return get_instance_references__geometry(params); + } } return {}; } -- cgit v1.2.3 From 3b1a16833b5834d768eacbcfdff4ee000a8d1af5 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Mon, 6 Sep 2021 09:40:06 -0700 Subject: UI: Area Split and Join Mouse Cursor Feedback This patch just changes the mouse cursor to a "stop sign" when dragging to an unsupported location during Join or Split operations. See D11396 for details and examples. Differential Revision: https://developer.blender.org/D11396 Reviewed by Campbell Barton --- source/blender/editors/screen/screen_ops.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 75f1baf30bb..5d6f21f4854 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1134,14 +1134,22 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) if ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) && (screen_geom_area_map_find_active_scredge( AREAMAP_FROM_SCREEN(screen), &screen_rect, event->x, event->y) == NULL)) { - /* Are we still in same area? */ - if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) { + + /* What area are we now in? */ + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + + if (area == sad->sa1) { /* Same area, so possible split. */ WM_cursor_set(win, SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); is_gesture = (delta_max > split_threshold); } + else if (!area || area->global) { + /* No area or Top bar or Status bar. */ + WM_cursor_set(win, WM_CURSOR_STOP); + is_gesture = false; + } else { /* Different area, so possible join. */ if (sad->gesture_dir == SCREEN_DIR_N) { @@ -1161,7 +1169,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - WM_cursor_set(CTX_wm_window(C), WM_CURSOR_CROSS); + WM_cursor_set(win, WM_CURSOR_CROSS); is_gesture = false; } } -- cgit v1.2.3 From 08593e46a3392a78420f3ec2a871aec9228c9d42 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 6 Sep 2021 18:46:13 +0200 Subject: Fix T91187: incorrect socket identifier --- source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 582b1a88221..5d3d75b4678 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -31,7 +31,11 @@ static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Factor"); - b.add_input("Factor").default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input("Factor", "Factor_001") + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); b.add_input("A"); b.add_input("A", "A_001"); b.add_input("A", "A_002"); -- cgit v1.2.3 From d7d8eb7de468dc2b3940d1ebb81cb16cdadbce3f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 6 Sep 2021 18:56:01 +0200 Subject: GPencil: Change icons missing in previous commit It was missing to change the modifier itself, not only RNA enum list. --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 06e8f7db0af..55fe74cab4f 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2902,7 +2902,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Line Art Modifier", "Generate line art strokes from selected source"); RNA_def_struct_sdna(srna, "LineartGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT); + RNA_def_struct_ui_icon(srna, ICON_MOD_LINEART); RNA_define_lib_overridable(true); @@ -3199,7 +3199,7 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) srna = RNA_def_struct(brna, "LengthGpencilModifier", "GpencilModifier"); RNA_def_struct_ui_text(srna, "Length Modifier", "Stretch or shrink strokes"); RNA_def_struct_sdna(srna, "LengthGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_MOD_EDGESPLIT); + RNA_def_struct_ui_icon(srna, ICON_MOD_LENGTH); RNA_define_lib_overridable(true); -- cgit v1.2.3 From 8cc3d2d6f51f9f05c568104be17c6643edfff101 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 6 Sep 2021 17:37:04 +0200 Subject: ID management: add options to force make local or force copy IDs when making them local. This is to be used when calling code already knows whether the 'made local' linked ID should be copied, or can directly be converted to a local one. Currently unused , this is preparation for rewrite of append code. --- source/blender/blenkernel/BKE_lib_id.h | 5 +++++ source/blender/blenkernel/intern/brush.c | 20 +++++++++++++------- source/blender/blenkernel/intern/lib_id.c | 18 ++++++++++++------ source/blender/blenkernel/intern/object.c | 18 ++++++++++++------ 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index a50faedcc3c..7fa21cc0656 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -237,6 +237,11 @@ enum { /** Making that ID local is part of making local a whole library. */ LIB_ID_MAKELOCAL_FULL_LIBRARY = 1 << 0, + /** In case caller code already knows this ID should be made local without copying. */ + LIB_ID_MAKELOCAL_FORCE_LOCAL = 1 << 1, + /** In case caller code already knows this ID should be made local using copying. */ + LIB_ID_MAKELOCAL_FORCE_COPY = 1 << 2, + /* Special type-specific options. */ /** For Objects, do not clear the proxy pointers while making the data-block local. */ LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index d60ef28efda..56c22df3b02 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -142,8 +142,16 @@ static void brush_free_data(ID *id) static void brush_make_local(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + Brush *brush = (Brush *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; + const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -151,19 +159,17 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) * - mixed: make copy */ - if (!ID_IS_LINKED(brush)) { - return; - } - if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ BKE_lib_id_make_local(bmain, &brush->clone.image->id, false, 0); } - BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); + if (!force_local && !force_copy) { + BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); + } - if (lib_local || is_local) { - if (!is_lib) { + if (lib_local || is_local || force_copy || force_local) { + if (!is_lib || force_local) { BKE_lib_id_clear_library_data(bmain, &brush->id); BKE_lib_id_expand_local(bmain, &brush->id); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 11e9053df43..cd0c3635dac 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -406,7 +406,15 @@ static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) */ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; + const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -416,14 +424,12 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) * we always want to localize, and we skip remapping (done later). */ - if (!ID_IS_LINKED(id)) { - return; + if (!force_copy && !force_local) { + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); } - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - if (lib_local || is_local) { - if (!is_lib) { + if (lib_local || is_local || force_copy || force_local) { + if (!is_lib || force_local) { BKE_lib_id_clear_library_data(bmain, id); BKE_lib_id_expand_local(bmain, id); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index bca1afbf101..4fa211604bc 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -324,9 +324,17 @@ static void object_free_data(ID *id) static void object_make_local(Main *bmain, ID *id, const int flags) { + if (!ID_IS_LINKED(id)) { + return; + } + Object *ob = (Object *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; + const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + BLI_assert(force_copy == false || force_copy != force_local); + bool is_local = false, is_lib = false; /* - only lib users: do nothing (unless force_local is set) @@ -336,14 +344,12 @@ static void object_make_local(Main *bmain, ID *id, const int flags) * we always want to localize, and we skip remapping (done later). */ - if (!ID_IS_LINKED(ob)) { - return; + if (!force_local && !force_copy) { + BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); } - BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); - - if (lib_local || is_local) { - if (!is_lib) { + if (lib_local || is_local || force_copy || force_local) { + if (!is_lib || force_local) { BKE_lib_id_clear_library_data(bmain, &ob->id); BKE_lib_id_expand_local(bmain, &ob->id); if (clear_proxy) { -- cgit v1.2.3 From c2ce68a0187ff8d6c8d0beafa0a3d655899a1d8f Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 7 Sep 2021 13:18:59 +0200 Subject: Fix T91238: crash when instancing geometry group input --- source/blender/nodes/geometry/nodes/node_geo_point_instance.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 9f67638de7e..f5ab9c053c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -121,6 +121,7 @@ static Vector get_instance_references__collection(GeoNodeExec static Vector get_instance_references__geometry(GeoNodeExecParams ¶ms) { GeometrySet geometry_set = params.extract_input("Instance Geometry"); + geometry_set.ensure_owns_direct_data(); return {std::move(geometry_set)}; } -- cgit v1.2.3 From 2b4afcbb4cdc231aebe343cd444d001b9f191204 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 7 Sep 2021 13:03:06 +0200 Subject: Fix T91236: AssetBrowser crash with certain collections Caused by {rB5a9a16334c57} Linking/appending an asset made from a collection containing certain types of objects lacking bounding boxes (camera, light) would crash. Add simple bbox check to prevent the crash. Maniphest Tasks: T91236 Differential Revision: https://developer.blender.org/D12415 --- source/blender/blenkernel/intern/object.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 4fa211604bc..394245b3a2c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5749,6 +5749,8 @@ void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) ob->data = new_data; ob->runtime.geometry_set_eval = NULL; ob->runtime.data_eval = NULL; - ob->runtime.bb->flag |= BOUNDBOX_DIRTY; + if (ob->runtime.bb != NULL) { + ob->runtime.bb->flag |= BOUNDBOX_DIRTY; + } ob->id.py_instance = NULL; } -- cgit v1.2.3 From 08acbdc1ff75607001f770b4281f53ec87449b7f Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Tue, 7 Sep 2021 11:03:48 -0300 Subject: Fix T91219: Crash canceling scale keyframes in dope sheet grease pencil Caused by {rBb0d9e6797fb8} Ideally `td->loc` should always be set and point to a 3d array. --- .../editors/transform/transform_convert_action.c | 20 +++++++++++--------- .../blender/editors/transform/transform_generics.c | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c index 075db30fa61..a6658ae00a3 100644 --- a/source/blender/editors/transform/transform_convert_action.c +++ b/source/blender/editors/transform/transform_convert_action.c @@ -51,7 +51,10 @@ /* helper struct for gp-frame transforms */ typedef struct tGPFtransdata { - float val; /* where transdata writes transform */ + union { + float val; /* where transdata writes transform */ + float loc[3]; /* #td->val and #td->loc share the same pointer. */ + }; int *sdata; /* pointer to gpf->framenum */ } tGPFtransdata; @@ -245,8 +248,8 @@ static int GPLayerToTransData(TransData *td, tfd->val = (float)gpf->framenum; tfd->sdata = &gpf->framenum; - td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */ - td->ival = td->iloc[0] = (float)gpf->framenum; + td->val = td->loc = &tfd->val; + td->ival = td->iloc[0] = tfd->val; td->center[0] = td->ival; td->center[1] = ypos; @@ -279,16 +282,15 @@ static int MaskLayerToTransData(TransData *td, masklay_shape = masklay_shape->next) { if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) { if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { - /* memory is calloc'ed, so that should zero everything nicely for us */ - td->val = &tfd->val; - td->ival = (float)masklay_shape->frame; + tfd->val = (float)masklay_shape->frame; + tfd->sdata = &masklay_shape->frame; + + td->val = td->loc = &tfd->val; + td->ival = td->iloc[0] = tfd->val; td->center[0] = td->ival; td->center[1] = ypos; - tfd->val = (float)masklay_shape->frame; - tfd->sdata = &masklay_shape->frame; - /* advance td now */ td++; tfd++; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 9f5e74db501..0dfb229191c 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -791,7 +791,7 @@ static void restoreElement(TransData *td) { transdata_restore_basic((TransDataBasic *)td); - if (td->val) { + if (td->val && td->val != td->loc) { *td->val = td->ival; } -- cgit v1.2.3 From 73ef2fc2f4e43a56d4b6965845534cc4df76610a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 7 Sep 2021 16:06:25 +0200 Subject: Fix T91093: off by one error in when resampling curve The bug existed in the Curve Resample and Curve to Points node. Differential Revision: https://developer.blender.org/D12416 --- source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 0b71a8cb03a..1382efa025b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -169,7 +169,7 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { const float length = input_splines[i]->length(); - const int count = std::max(int(length / *mode_param.length), 1); + const int count = std::max(int(length / *mode_param.length) + 1, 1); output_splines[i] = resample_spline(*input_splines[i], count); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 052eb92d269..17cd8e987a7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -101,7 +101,7 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, int offset = 0; for (const int i : IndexRange(size)) { offsets[i] = offset; - offset += splines[i]->length() / resolution; + offset += splines[i]->length() / resolution + 1; } offsets.last() = offset; return offsets; -- cgit v1.2.3 From a392609ab612236401b335f5c7f2790f60a4f765 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 7 Sep 2021 13:28:14 -0500 Subject: Cleanup: Move function to versioning_common.cc --- source/blender/blenloader/intern/versioning_290.c | 27 --------------------- source/blender/blenloader/intern/versioning_300.c | 27 --------------------- .../blender/blenloader/intern/versioning_common.cc | 28 ++++++++++++++++++++++ .../blender/blenloader/intern/versioning_common.h | 6 +++++ 4 files changed, 34 insertions(+), 54 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index b217850e119..cb7a8ad592a 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -829,33 +829,6 @@ static void do_versions_strip_cache_settings_recursive(const ListBase *seqbase) } } -static void version_node_socket_name(bNodeTree *ntree, - const int node_type, - const char *old_name, - const char *new_name) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - } - } -} - static void version_node_join_geometry_for_multi_input_socket(bNodeTree *ntree) { LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 13577164f20..f050de6e6e9 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -526,33 +526,6 @@ static void version_switch_node_input_prefix(Main *bmain) FOREACH_NODETREE_END; } -static void version_node_socket_name(bNodeTree *ntree, - const int node_type, - const char *old_name, - const char *new_name) -{ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - strcpy(socket->name, new_name); - } - if (STREQ(socket->identifier, old_name)) { - strcpy(socket->identifier, new_name); - } - } - } - } -} - static bool replace_bbone_len_scale_rnapath(char **p_old_path, int *p_index) { char *old_path = *p_old_path; diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 208c02b60d1..3f13d1ec12e 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -22,6 +22,7 @@ #include +#include "DNA_node_types.h" #include "DNA_screen_types.h" #include "BLI_listbase.h" @@ -85,3 +86,30 @@ ID *do_versions_rename_id(Main *bmain, } return id; } + +void version_node_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } + } + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index 47e0b74a3e4..c1fe2b591cd 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -23,6 +23,7 @@ struct ARegion; struct ListBase; struct Main; +struct bNodeTree; #ifdef __cplusplus extern "C" { @@ -38,6 +39,11 @@ ID *do_versions_rename_id(Main *bmain, const char *name_src, const char *name_dst); +void version_node_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); + #ifdef __cplusplus } #endif -- cgit v1.2.3 From adbafe3b43d6131c74d96128b23ed24ae904d487 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Tue, 7 Sep 2021 20:10:53 +0100 Subject: Animation: Implement generic slider in graph_slider_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements the generic slider from `ed_draw.c` to the `GRAPH_OT_decimate` operator This draws a useful UI and enables precision mode and stepping Overshoot is disabled The status message is moved to the workspace footer Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D9361 Ref: D9361 --- .../blender/editors/space_graph/graph_slider_ops.c | 46 ++++++++++------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 10629caa8b0..f04336cab84 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -41,6 +41,7 @@ #include "ED_keyframes_edit.h" #include "ED_numinput.h" #include "ED_screen.h" +#include "ED_util.h" #include "WM_api.h" #include "WM_types.h" @@ -94,6 +95,8 @@ typedef struct tDecimateGraphOp { /** The original bezt curve data (used for restoring fcurves). */ ListBase bezt_arr_list; + struct tSlider *slider; + NumInput num; } tDecimateGraphOp; @@ -161,6 +164,8 @@ static void decimate_exit(bContext *C, wmOperator *op) ScrArea *area = dgo->area; LinkData *link; + ED_slider_destroy(C, dgo->slider); + for (link = dgo->bezt_arr_list.first; link != NULL; link = link->next) { tBeztCopyData *copy = link->data; MEM_freeN(copy->bezt); @@ -178,11 +183,14 @@ static void decimate_exit(bContext *C, wmOperator *op) op->customdata = NULL; } -/* Draw a percentage indicator in header. */ -static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo) +/* Draw a percentage indicator in workspace footer. */ +static void decimate_draw_status(bContext *C, tDecimateGraphOp *dgo) { char status_str[UI_MAX_DRAW_STR]; char mode_str[32]; + char slider_string[UI_MAX_DRAW_STR]; + + ED_slider_status_string_get(dgo->slider, slider_string, UI_MAX_DRAW_STR); strcpy(mode_str, TIP_("Decimate Keyframes")); @@ -194,23 +202,10 @@ static void decimate_draw_status_header(wmOperator *op, tDecimateGraphOp *dgo) BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs); } else { - float percentage = RNA_property_float_get(op->ptr, dgo->percentage_prop); - BLI_snprintf( - status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(percentage * 100.0f)); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string); } - ED_area_status_text(dgo->area, status_str); -} - -/* Calculate percentage based on position of mouse (we only use x-axis for now. - * Since this is more convenient for users to do), and store new percentage value. - */ -static void decimate_mouse_update_percentage(tDecimateGraphOp *dgo, - wmOperator *op, - const wmEvent *event) -{ - float percentage = (event->x - dgo->region->winrct.xmin) / ((float)dgo->region->winx); - RNA_property_float_set(op->ptr, dgo->percentage_prop, percentage); + ED_workspace_status_text(C, status_str); } static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -234,10 +229,11 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); - /* Initialize percentage so that it will have the correct value before the first mouse move. */ - decimate_mouse_update_percentage(dgo, op, event); + dgo->slider = ED_slider_create(C); + ED_slider_init(dgo->slider, event); + ED_slider_allow_overshoot_set(dgo->slider, false); - decimate_draw_status_header(op, dgo); + decimate_draw_status(C, dgo); /* Construct a list with the original bezt arrays so we can restore them during modal operation. */ @@ -300,13 +296,14 @@ static void graphkeys_decimate_modal_update(bContext *C, wmOperator *op) * (e.g. pressing a key or moving the mouse). */ tDecimateGraphOp *dgo = op->customdata; - decimate_draw_status_header(op, dgo); + decimate_draw_status(C, dgo); /* Reset keyframe data (so we get back to the original state). */ decimate_reset_bezts(dgo); /* Apply... */ - float remove_ratio = RNA_property_float_get(op->ptr, dgo->percentage_prop); + float remove_ratio = ED_slider_factor_get(dgo->slider); + RNA_property_float_set(op->ptr, dgo->percentage_prop, remove_ratio); /* We don't want to limit the decimation to a certain error margin. */ const float error_sq_max = FLT_MAX; decimate_graph_keys(&dgo->ac, remove_ratio, error_sq_max); @@ -323,6 +320,8 @@ static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent * const bool has_numinput = hasNumInput(&dgo->num); + ED_slider_modal(dgo->slider, event); + switch (event->type) { case LEFTMOUSE: /* Confirm */ case EVT_RETKEY: @@ -353,9 +352,6 @@ static int graphkeys_decimate_modal(bContext *C, wmOperator *op, const wmEvent * case MOUSEMOVE: /* Calculate new position. */ { if (has_numinput == false) { - /* Update percentage based on position of mouse. */ - decimate_mouse_update_percentage(dgo, op, event); - /* Update pose to reflect the new values. */ graphkeys_decimate_modal_update(C, op); } -- cgit v1.2.3 From 4abbf6d82b190e1f67e180bf4330e51a53267144 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 7 Sep 2021 23:39:50 +0200 Subject: Fix T90967: Snapping not working with single strip Even if `snap_targets` `SeqCollection` is empty, there can be static snap points defined, so don't condition snapping on non-zero target count. Differential Revision: https://developer.blender.org/D12400 --- source/blender/editors/transform/transform_snap_sequencer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index a54149912a9..e82a00bcc77 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -260,7 +260,7 @@ TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase); SeqCollection *snap_targets = query_snap_targets(t, snap_sources); - if (SEQ_collection_len(snap_sources) == 0 || SEQ_collection_len(snap_targets) == 0) { + if (SEQ_collection_len(snap_sources) == 0) { SEQ_collection_free(snap_targets); SEQ_collection_free(snap_sources); MEM_freeN(snap_data); -- cgit v1.2.3 From e6a1d488ab7968729ec450f7f5674a49a555dbbd Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 8 Sep 2021 00:01:00 +0200 Subject: Cleanup: VSE select operator Operator combines more features and it wasn't very transparent which properties are used in which feature and how they are used. Features linked_time, side_of_frame and linked_handle are isolated, logic moved into own function. deselect_all property was removed as it practically equals to !extend in all cases. Functional change: Dragging existing selection will not change active strip. Previously this could happen if click happened on strip that wasn't active. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12320 --- .../keyconfig/keymap_data/blender_default.py | 3 +- .../editors/space_sequencer/sequencer_select.c | 402 ++++++++++----------- 2 files changed, 202 insertions(+), 203 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 6a9306c2eab..a1ae31746fd 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2606,8 +2606,7 @@ def km_sequencer(params): for i in range(10) ) ), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 4c938a412d2..80d3e2cbdaa 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -504,238 +504,245 @@ void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot) /** \name Select Operator * \{ */ -static int sequencer_select_exec(bContext *C, wmOperator *op) +static void sequencer_select_set_active(Scene *scene, Sequence *seq) { - View2D *v2d = UI_view2d_fromcontext(C); - Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - const bool linked_handle = RNA_boolean_get(op->ptr, "linked_handle"); - const bool linked_time = RNA_boolean_get(op->ptr, "linked_time"); - bool side_of_frame = RNA_boolean_get(op->ptr, "side_of_frame"); - bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - int mval[2]; - int ret_value = OPERATOR_CANCELLED; - mval[0] = RNA_int_get(op->ptr, "mouse_x"); - mval[1] = RNA_int_get(op->ptr, "mouse_y"); - - Sequence *seq, *neighbor, *act_orig; - int hand, sel_side; + SEQ_select_active_set(scene, seq); - if (ed == NULL) { - return OPERATOR_CANCELLED; + if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { + if (seq->strip) { + BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR); + } } - - if (extend) { - wait_to_deselect_others = false; + else if (seq->type == SEQ_TYPE_SOUND_RAM) { + if (seq->strip) { + BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR); + } } + recurs_sel_seq(seq); +} - seq = find_nearest_seq(scene, v2d, &hand, mval); +static void sequencer_select_do_updates(bContext *C, Scene *scene) +{ + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); +} - /* XXX: not nice, Ctrl+RMB needs to do side_of_frame only when not over a strip. */ - if (seq && linked_time) { - side_of_frame = false; - } +static void sequencer_select_side_of_frame(const bContext *C, + const View2D *v2d, + const int mval[2], + Scene *scene) +{ + Editing *ed = SEQ_editing_get(scene); - /* Select left, right or overlapping the current frame. */ - if (side_of_frame) { - /* Use different logic for this. */ - if (extend == false) { - ED_sequencer_deselect_all(scene); + const float x = UI_view2d_region_to_view_x(v2d, mval[0]); + LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { + if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) || + ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) { + /* Select left or right. */ + seq_iter->flag |= SELECT; + recurs_sel_seq(seq_iter); } + } - const float x = UI_view2d_region_to_view_x(v2d, mval[0]); + { + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq && sseq->flag & SEQ_MARKER_TRANS) { + TimeMarker *tmarker; - LISTBASE_FOREACH (Sequence *, seq_iter, SEQ_active_seqbase_get(ed)) { - if (((x < CFRA) && (seq_iter->enddisp <= CFRA)) || - ((x >= CFRA) && (seq_iter->startdisp >= CFRA))) { - /* Select left or right. */ - seq_iter->flag |= SELECT; - recurs_sel_seq(seq_iter); + for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) { + if (((x < CFRA) && (tmarker->frame <= CFRA)) || + ((x >= CFRA) && (tmarker->frame >= CFRA))) { + tmarker->flag |= SELECT; + } + else { + tmarker->flag &= ~SELECT; + } } } + } +} - { - SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq && sseq->flag & SEQ_MARKER_TRANS) { - TimeMarker *tmarker; +static void sequencer_select_linked_handle(const bContext *C, + Sequence *seq, + const int handle_clicked) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + if (!ELEM(handle_clicked, SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT)) { + /* First click selects the strip and its adjacent handles (if valid). + * Second click selects the strip, + * both of its handles and its adjacent handles (if valid). */ + const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT); + seq->flag &= ~SEQ_ALLSEL; + seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT; + select_surrounding_handles(scene, seq); + } + else { + /* Always select the strip under the cursor. */ + seq->flag |= SELECT; - for (tmarker = scene->markers.first; tmarker; tmarker = tmarker->next) { - if (((x < CFRA) && (tmarker->frame <= CFRA)) || - ((x >= CFRA) && (tmarker->frame >= CFRA))) { - tmarker->flag |= SELECT; + /* First click selects adjacent handles on that side. + * Second click selects all strips in that direction. + * If there are no adjacent strips, it just selects all in that direction. + */ + int sel_side = handle_clicked; + Sequence *neighbor = find_neighboring_sequence(scene, seq, sel_side, -1); + if (neighbor) { + switch (sel_side) { + case SEQ_SIDE_LEFT: + if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) { + seq->flag |= SELECT; + select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp); } else { - tmarker->flag &= ~SELECT; + seq->flag |= SELECT; + neighbor->flag |= SELECT; + recurs_sel_seq(neighbor); + neighbor->flag |= SEQ_RIGHTSEL; + seq->flag |= SEQ_LEFTSEL; } - } + break; + case SEQ_SIDE_RIGHT: + if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) { + seq->flag |= SELECT; + select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp); + } + else { + seq->flag |= SELECT; + neighbor->flag |= SELECT; + recurs_sel_seq(neighbor); + neighbor->flag |= SEQ_LEFTSEL; + seq->flag |= SEQ_RIGHTSEL; + } + break; } } + else { - ret_value = OPERATOR_FINISHED; + select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); + } } - else { - act_orig = ed->act_seq; - - if (seq) { - /* Are we trying to select a handle that's already selected? */ - const bool handle_selected = ((hand == SEQ_SIDE_LEFT) && (seq->flag & SEQ_LEFTSEL)) || - ((hand == SEQ_SIDE_RIGHT) && (seq->flag & SEQ_RIGHTSEL)); - - if (wait_to_deselect_others && (seq->flag & SELECT) && - (hand == SEQ_SIDE_NONE || handle_selected)) { - ret_value = OPERATOR_RUNNING_MODAL; - } - else if (!extend && !linked_handle) { - ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; - } - else { - ret_value = OPERATOR_FINISHED; - } - - SEQ_select_active_set(scene, seq); +} - if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) { - if (seq->strip) { - BLI_strncpy(ed->act_imagedir, seq->strip->dir, FILE_MAXDIR); - } - } - else if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (seq->strip) { - BLI_strncpy(ed->act_sounddir, seq->strip->dir, FILE_MAXDIR); - } - } +static bool element_already_selected(const Sequence *seq, const int handle_clicked) +{ + const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) && + (seq->flag & SEQ_LEFTSEL)) || + ((handle_clicked == SEQ_SIDE_RIGHT) && + (seq->flag & SEQ_RIGHTSEL)); + return ((seq->flag & SELECT) && handle_clicked == SEQ_SIDE_NONE) || handle_already_selected; +} - /* On Alt selection, select the strip and bordering handles. */ - if (linked_handle) { - if (!ELEM(hand, SEQ_SIDE_LEFT, SEQ_SIDE_RIGHT)) { - /* First click selects the strip and its adjacent handles (if valid). - * Second click selects the strip, - * both of its handles and its adjacent handles (if valid). */ - const bool is_striponly_selected = ((seq->flag & SEQ_ALLSEL) == SELECT); +static void sequencer_select_strip_impl(const Editing *ed, + Sequence *seq, + const int handle_clicked, + const bool extend) +{ + /* Deselect strip. */ + if (extend && (seq->flag & SELECT) && ed->act_seq == seq) { + switch (handle_clicked) { + case SEQ_SIDE_NONE: + seq->flag &= ~SEQ_ALLSEL; + break; + case SEQ_SIDE_LEFT: + seq->flag ^= SEQ_LEFTSEL; + break; + case SEQ_SIDE_RIGHT: + seq->flag ^= SEQ_RIGHTSEL; + break; + } + } + else { /* Select strip. */ + seq->flag |= SELECT; + if (handle_clicked == SEQ_SIDE_LEFT) { + seq->flag |= SEQ_LEFTSEL; + } + if (handle_clicked == SEQ_SIDE_RIGHT) { + seq->flag |= SEQ_RIGHTSEL; + } + } +} - if (!extend) { - ED_sequencer_deselect_all(scene); - } - seq->flag &= ~SEQ_ALLSEL; - seq->flag |= is_striponly_selected ? SEQ_ALLSEL : SELECT; - select_surrounding_handles(scene, seq); - } - else { - /* Always select the strip under the cursor. */ - seq->flag |= SELECT; +static int sequencer_select_exec(bContext *C, wmOperator *op) +{ + View2D *v2d = UI_view2d_fromcontext(C); + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + const bool extend = RNA_boolean_get(op->ptr, "extend"); - /* First click selects adjacent handles on that side. - * Second click selects all strips in that direction. - * If there are no adjacent strips, it just selects all in that direction. - */ - sel_side = hand; - neighbor = find_neighboring_sequence(scene, seq, sel_side, -1); - if (neighbor) { - switch (sel_side) { - case SEQ_SIDE_LEFT: - if ((seq->flag & SEQ_LEFTSEL) && (neighbor->flag & SEQ_RIGHTSEL)) { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - select_active_side(ed->seqbasep, SEQ_SIDE_LEFT, seq->machine, seq->startdisp); - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - neighbor->flag |= SELECT; - recurs_sel_seq(neighbor); - neighbor->flag |= SEQ_RIGHTSEL; - seq->flag |= SEQ_LEFTSEL; - } - break; - case SEQ_SIDE_RIGHT: - if ((seq->flag & SEQ_RIGHTSEL) && (neighbor->flag & SEQ_LEFTSEL)) { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - select_active_side(ed->seqbasep, SEQ_SIDE_RIGHT, seq->machine, seq->startdisp); - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - seq->flag |= SELECT; - - neighbor->flag |= SELECT; - recurs_sel_seq(neighbor); - neighbor->flag |= SEQ_LEFTSEL; - seq->flag |= SEQ_RIGHTSEL; - } - break; - } - } - else { - if (extend == 0) { - ED_sequencer_deselect_all(scene); - } - select_active_side(ed->seqbasep, sel_side, seq->machine, seq->startdisp); - } - } + if (ed == NULL) { + return OPERATOR_CANCELLED; + } - ret_value = OPERATOR_FINISHED; - } - else { - if (extend && (seq->flag & SELECT) && ed->act_seq == act_orig) { - switch (hand) { - case SEQ_SIDE_NONE: - if (linked_handle == 0) { - seq->flag &= ~SEQ_ALLSEL; - } - break; - case SEQ_SIDE_LEFT: - seq->flag ^= SEQ_LEFTSEL; - break; - case SEQ_SIDE_RIGHT: - seq->flag ^= SEQ_RIGHTSEL; - break; - } - ret_value = OPERATOR_FINISHED; - } - else { - seq->flag |= SELECT; - if (hand == SEQ_SIDE_LEFT) { - seq->flag |= SEQ_LEFTSEL; - } - if (hand == SEQ_SIDE_RIGHT) { - seq->flag |= SEQ_RIGHTSEL; - } - } - } + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); - recurs_sel_seq(seq); + int handle_clicked; + Sequence *seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); - if (linked_time) { - select_linked_time(ed->seqbasep, seq); - } + /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, + * therefore both properties can be true at the same time. */ + if (seq && RNA_boolean_get(op->ptr, "linked_time")) { + if (!extend) { + ED_sequencer_deselect_all(scene); + } + sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + select_linked_time(ed->seqbasep, seq); + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); + return OPERATOR_FINISHED; + } - BLI_assert((ret_value & OPERATOR_CANCELLED) == 0); + /* Select left, right or overlapping the current frame. */ + if (RNA_boolean_get(op->ptr, "side_of_frame")) { + if (!extend) { + ED_sequencer_deselect_all(scene); } - else if (deselect_all) { + sequencer_select_side_of_frame(C, v2d, mval, scene); + sequencer_select_do_updates(C, scene); + return OPERATOR_FINISHED; + } + + /* On Alt selection, select the strip and bordering handles. */ + if (seq && RNA_boolean_get(op->ptr, "linked_handle")) { + if (!extend) { ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; } + sequencer_select_linked_handle(C, seq, handle_clicked); + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); + return OPERATOR_FINISHED; } - ED_outliner_select_sync_from_sequence_tag(C); + const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + /* Clicking on already selected element falls on modal operation. + * All strips are deselected on mouse button release unless extend mode is used. */ + if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !extend) { + return OPERATOR_RUNNING_MODAL; + } + + int ret_value = OPERATOR_CANCELLED; + if (!extend) { + ED_sequencer_deselect_all(scene); + ret_value = OPERATOR_FINISHED; + } + + /* Nothing to select, but strips could be deselected. */ + if (!seq) { + sequencer_select_do_updates(C, scene); + return ret_value; + } + + /* Do actual selection. */ + sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + ret_value = OPERATOR_FINISHED; + sequencer_select_do_updates(C, scene); + sequencer_select_set_active(scene, seq); return ret_value; } @@ -763,13 +770,6 @@ void SEQUENCER_OT_select(wmOperatorType *ot) prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "linked_handle", false, -- cgit v1.2.3 From 1680c3d5104d5c33a18790023b1b29d16106afe2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 7 Sep 2021 17:05:58 -0500 Subject: UI: Remove label of mode in Point Instance node There isn't enough space for this label at the default node width, and it was obvious what the drop-down is doing anyway. --- source/blender/nodes/geometry/nodes/node_geo_point_instance.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index f5ab9c053c1..72e9e8f5c29 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -38,7 +38,7 @@ static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "instance_type", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "instance_type", 0, "", ICON_NONE); if (RNA_enum_get(ptr, "instance_type") == GEO_NODE_POINT_INSTANCE_TYPE_COLLECTION) { uiItemR(layout, ptr, "use_whole_collection", 0, nullptr, ICON_NONE); } -- cgit v1.2.3 From 7beb4a0e0a49fdee3c4b98bcf9287ab72aaebda5 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Wed, 8 Sep 2021 01:19:24 +0200 Subject: Fix non-exiting property in keymap In e6a1d488ab79 `deselect_all` property was removed from `sequencer.select` operator but some keymap items were missed, which was caught by tests by buildbot. --- release/scripts/presets/keyconfig/keymap_data/blender_default.py | 3 +-- .../scripts/presets/keyconfig/keymap_data/industry_compatible_data.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index a1ae31746fd..44b77ab2aac 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6996,8 +6996,7 @@ def km_sequencer_editor_tool_select(params): "Sequencer Tool: Select", {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, - {"properties": [("deselect_all", not params.legacy)]}), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), *_template_items_change_frame(params), ]}, ) diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index dba94d71a43..dbe351eb10c 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1814,8 +1814,7 @@ def km_sequencer(params): for i in range(10) ) ), - ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, - {"properties": [("deselect_all", True)]}), + ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), ("sequencer.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, -- cgit v1.2.3 From 8d40d61af093fcf528ce282b740eca119a5ee5d3 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Tue, 7 Sep 2021 16:43:17 +0200 Subject: Fix T91225: Quick Favorites and shortcuts are broken for some properties Caused by {rB3e4d720ae483}. Before above commit, this had a different path handling and RNA_path_from_ID_to_struct() was always used [which kind of took care of this]. Now this is only used if ptr represents an ID itself, so we are "loosing" part of the path. This patch adds the path back on the member_id in wm_context_member_from_ptr() for everthing related to space_data, so WM_context_path_resolve_property_full() can construct a full path even for these. Maniphest Tasks: T91225 Differential Revision: https://developer.blender.org/D12418 --- source/blender/windowmanager/intern/wm_operators.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index df051328990..81dcc5ccea0 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -552,37 +552,38 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr const View3D *v3d = (View3D *)space_data; const View3DShading *shading = &v3d->shading; - TEST_PTR_DATA_TYPE("space_data", RNA_View3DOverlay, ptr, v3d); - TEST_PTR_DATA_TYPE("space_data", RNA_View3DShading, ptr, shading); + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); + TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); break; } case SPACE_GRAPH: { const SpaceGraph *sipo = (SpaceGraph *)space_data; const bDopeSheet *ads = sipo->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } case SPACE_FILE: { const SpaceFile *sfile = (SpaceFile *)space_data; const FileSelectParams *params = ED_fileselect_get_active_params(sfile); - TEST_PTR_DATA_TYPE("space_data", RNA_FileSelectParams, ptr, params); + TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); break; } case SPACE_IMAGE: { const SpaceImage *sima = (SpaceImage *)space_data; - TEST_PTR_DATA_TYPE("space_data", RNA_SpaceUVEditor, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); break; } case SPACE_NLA: { const SpaceNla *snla = (SpaceNla *)space_data; const bDopeSheet *ads = snla->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } case SPACE_ACTION: { const SpaceAction *sact = (SpaceAction *)space_data; const bDopeSheet *ads = &sact->ads; - TEST_PTR_DATA_TYPE("space_data", RNA_DopeSheet, ptr, ads); + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); break; } } -- cgit v1.2.3 From 54f5c174a8cf480d934f3be8ecc85c76537ad148 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 8 Sep 2021 08:39:03 +0200 Subject: Asset: Dropping Material assets on material slot under mouse cursor. This patch allows dropping material assets from material slot under the mouse cursor. Before this change the material slot had to be hand-picked from the properties panel. For consistency it is chosen to do this in any shading mode as the tooltip shows what is exactly going to happen during release. The feature also works for other object types than Meshes as it uses the drawn surface on the GPU to detect the material slots. Performance of this patch has been tested with AMD GCN3.0 cards and are very responsive. Reviewed By: fclem, Severin Differential Revision: https://developer.blender.org/D12190 --- source/blender/blenkernel/BKE_object.h | 2 + source/blender/blenkernel/intern/object.c | 6 ++ source/blender/draw/DRW_engine.h | 1 + source/blender/draw/engines/basic/basic_engine.c | 75 +++++++++++++++++----- source/blender/draw/intern/DRW_render.h | 1 + source/blender/draw/intern/draw_manager.c | 7 ++ source/blender/draw/intern/draw_manager.h | 1 + source/blender/editors/include/ED_view3d.h | 10 +++ source/blender/editors/object/object_relations.c | 20 +++--- .../blender/editors/space_view3d/view3d_select.c | 62 ++++++++++++++---- source/blender/editors/space_view3d/view3d_view.c | 25 ++++++-- 11 files changed, 166 insertions(+), 44 deletions(-) diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 31b3cd66cbb..0e153c5a82a 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -463,6 +463,8 @@ void BKE_object_replace_data_on_shallow_copy(struct Object *ob, struct ID *new_d struct PartEff; struct PartEff *BKE_object_do_version_give_parteff_245(struct Object *ob); +bool BKE_object_supports_material_slots(struct Object *ob); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 394245b3a2c..d0d1db9b4f8 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5754,3 +5754,9 @@ void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) } ob->id.py_instance = NULL; } + +bool BKE_object_supports_material_slots(struct Object *ob) +{ + return ELEM( + ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME); +} diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 8a35ab2aeb9..a125a13eaf9 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -110,6 +110,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, bool use_obedit_skip, bool draw_surface, bool use_nearest, + const bool do_material_sub_selection, const struct rcti *rect, DRW_SelectPassFn select_pass_fn, void *select_pass_user_data, diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index c120df7e897..87f5c6f5857 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -25,9 +25,12 @@ #include "DRW_render.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BLI_alloca.h" + #include "DNA_particle_types.h" #include "GPU_shader.h" @@ -80,6 +83,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ /* Functions */ @@ -131,6 +135,8 @@ static void basic_cache_init(void *vedata) stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); } + stl->g_data->use_material_slot_selection = DRW_state_is_material_select(); + /* Twice for normal and in front objects. */ for (int i = 0; i < 2; i++) { DRWState clip_state = (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? DRW_STATE_CLIP_PLANES : 0; @@ -155,6 +161,38 @@ static void basic_cache_init(void *vedata) } } +/* TODO(fclem): DRW_cache_object_surface_material_get needs a refactor to allow passing NULL + * instead of gpumat_array. Avoiding all this boilerplate code. */ +static struct GPUBatch **basic_object_surface_material_get(Object *ob) +{ + const int materials_len = DRW_cache_object_material_count_get(ob); + struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); + memset(gpumat_array, 0, sizeof(*gpumat_array) * materials_len); + + return DRW_cache_object_surface_material_get(ob, gpumat_array, materials_len); +} + +static void basic_cache_populate_particles(void *vedata, Object *ob) +{ + const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; + BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; + for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + ParticleSettings *part = psys->part; + const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + if (draw_as == PART_DRAW_PATH) { + struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); + if (stl->g_data->use_material_slot_selection) { + const short material_slot = part->omat; + DRW_select_load_id(ob->runtime.select_id | (material_slot << 16)); + } + DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL); + } + } +} + static void basic_cache_populate(void *vedata, Object *ob) { BASIC_StorageList *stl = ((BASIC_Data *)vedata)->stl; @@ -165,24 +203,13 @@ static void basic_cache_populate(void *vedata, Object *ob) return; } - bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; - const DRWContextState *draw_ctx = DRW_context_state_get(); if (ob != draw_ctx->object_edit) { - for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { - if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { - continue; - } - ParticleSettings *part = psys->part; - const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; - if (draw_as == PART_DRAW_PATH) { - struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); - DRW_shgroup_call(stl->g_data->depth_hair_shgrp[do_in_front], hairs, NULL); - } - } + basic_cache_populate_particles(vedata, ob); } /* Make flat object selectable in ortho view if wireframe is enabled. */ + const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; if ((draw_ctx->v3d->overlay.flag & V3D_OVERLAY_WIREFRAMES) || (draw_ctx->v3d->shading.type == OB_WIRE) || (ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE)) { int flat_axis = 0; @@ -211,9 +238,25 @@ static void basic_cache_populate(void *vedata, Object *ob) DRW_shgroup_call_sculpt(shgrp, ob, false, false); } else { - struct GPUBatch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - DRW_shgroup_call(shgrp, geom, ob); + if (stl->g_data->use_material_slot_selection && BKE_object_supports_material_slots(ob)) { + struct GPUBatch **geoms = basic_object_surface_material_get(ob); + if (geoms) { + const int materials_len = DRW_cache_object_material_count_get(ob); + for (int i = 0; i < materials_len; i++) { + if (geoms[i] == NULL) { + continue; + } + const short material_slot_select_id = i + 1; + DRW_select_load_id(ob->runtime.select_id | (material_slot_select_id << 16)); + DRW_shgroup_call(shgrp, geoms[i], ob); + } + } + } + else { + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(shgrp, geom, ob); + } } } } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 6639a100af9..660a4adaf51 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -731,6 +731,7 @@ void DRW_select_load_id(uint id); /* Draw State */ bool DRW_state_is_fbo(void); bool DRW_state_is_select(void); +bool DRW_state_is_material_select(void); bool DRW_state_is_depth(void); bool DRW_state_is_image_render(void); bool DRW_state_is_scene_render(void); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 9590a4aa7ee..aca645acc09 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2232,6 +2232,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, bool use_obedit_skip, bool draw_surface, bool UNUSED(use_nearest), + const bool do_material_sub_selection, const rcti *rect, DRW_SelectPassFn select_pass_fn, void *select_pass_user_data, @@ -2299,6 +2300,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DST.viewport = viewport; DST.options.is_select = true; + DST.options.is_material_select = do_material_sub_selection; drw_task_graph_init(); /* Get list of enabled engines */ if (use_obedit) { @@ -2776,6 +2778,11 @@ bool DRW_state_is_select(void) return DST.options.is_select; } +bool DRW_state_is_material_select(void) +{ + return DST.options.is_material_select; +} + bool DRW_state_is_depth(void) { return DST.options.is_depth; diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 1747ca752c7..c09126c98ef 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -553,6 +553,7 @@ typedef struct DRWManager { struct { uint is_select : 1; + uint is_material_select : 1; uint is_depth : 1; uint is_image_render : 1; uint is_scene_render : 1; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 2c958d282f9..cf8dcbd7995 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -566,6 +566,13 @@ eV3DSelectObjectFilter ED_view3d_select_filter_from_mode(const struct Scene *sce void view3d_opengl_select_cache_begin(void); void view3d_opengl_select_cache_end(void); +int view3d_opengl_select_ex(struct ViewContext *vc, + unsigned int *buffer, + unsigned int bufsize, + const struct rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter, + const bool do_material_slot_selection); int view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, @@ -638,6 +645,9 @@ void ED_view3d_draw_setup_view(const struct wmWindowManager *wm, struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); struct Object *ED_view3d_give_object_under_cursor(struct bContext *C, const int mval[2]); +struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C, + const int mval[2], + int *r_material_slot); bool ED_view3d_is_object_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *area, struct ARegion *region, bool do_clip); void ED_view3d_update_viewmat(struct Depsgraph *depsgraph, diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index ec72ff11683..75269dffec8 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2729,25 +2729,26 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, const wmEvent *event) { - Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + int mat_slot = 0; + Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); if (ob == NULL) { return BLI_strdup(""); } + mat_slot = max_ii(mat_slot, 1); char name[MAX_ID_NAME - 2]; RNA_string_get(properties, "name", name); - int active_mat_slot = max_ii(ob->actcol, 1); - Material *prev_mat = BKE_object_material_get(ob, active_mat_slot); + Material *prev_mat = BKE_object_material_get(ob, mat_slot); char *result; if (prev_mat) { const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)"); - result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot, prev_mat->id.name + 2); } else { const char *tooltip = TIP_("Drop %s on %s (slot %d)"); - result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot); + result = BLI_sprintfN(tooltip, name, ob->id.name + 2, mat_slot); } return result; } @@ -2755,7 +2756,10 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); - Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + int mat_slot = 0; + Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); + mat_slot = max_ii(mat_slot, 1); + Material *ma; char name[MAX_ID_NAME - 2]; @@ -2765,9 +2769,7 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - const short active_mat_slot = ob->actcol; - - BKE_object_material_assign(CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF); + BKE_object_material_assign(CTX_data_main(C), ob, ma, mat_slot, BKE_MAT_ASSIGN_USERPREF); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index e3f97dd1c63..ff98762e373 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1953,7 +1953,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, const int mval[2], eV3DSelectObjectFilter select_filter, bool do_nearest, - bool do_nearest_xray_if_supported) + bool do_nearest_xray_if_supported, + const bool do_material_slot_selection) { rcti rect; int hits15, hits9 = 0, hits5 = 0; @@ -1972,7 +1973,8 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc, view3d_opengl_select_cache_begin(); BLI_rcti_init_pt_radius(&rect, mval, 14); - hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter); + hits15 = view3d_opengl_select_ex( + vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter, do_material_slot_selection); if (hits15 == 1) { hits = selectbuffer_ret_hits_15(buffer, hits15); goto finally; @@ -2071,7 +2073,8 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, do_nearest = do_nearest && !enumerate; - int hits = mixed_bones_object_selectbuffer(vc, buffer, mval, select_filter, do_nearest, true); + int hits = mixed_bones_object_selectbuffer( + vc, buffer, mval, select_filter, do_nearest, true, false); return hits; } @@ -2088,12 +2091,14 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, int hits, Base *startbase, bool has_bones, - bool do_nearest) + bool do_nearest, + int *r_sub_selection) { ViewLayer *view_layer = vc->view_layer; View3D *v3d = vc->v3d; Base *base, *basact = NULL; int a; + int sub_selection_id = 0; if (do_nearest) { uint min = 0xFFFFFFFF; @@ -2105,6 +2110,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) { min = buffer[4 * a + 1]; selcol = buffer[4 * a + 3] & 0xFFFF; + sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; } } } @@ -2118,6 +2124,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) { min = buffer[4 * a + 1]; selcol = buffer[4 * a + 3] & 0xFFFF; + sub_selection_id = (buffer[4 * a + 3] & 0xFFFF0000) >> 16; } } } @@ -2184,11 +2191,16 @@ static Base *mouse_select_eval_buffer(ViewContext *vc, } } + if (basact && r_sub_selection) { + *r_sub_selection = sub_selection_id; + } + return basact; } -/* mval comes from event->mval, only use within region handlers */ -Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) +static Base *ed_view3d_give_base_under_cursor_ex(bContext *C, + const int mval[2], + int *r_material_slot) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewContext vc; @@ -2202,18 +2214,30 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) ED_view3d_viewcontext_init(C, &vc, depsgraph); const bool do_nearest = !XRAY_ACTIVE(vc.v3d); + const bool do_material_slot_selection = r_material_slot != NULL; const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false); + &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection); if (hits > 0) { - const bool has_bones = selectbuffer_has_bones(buffer, hits); - basact = mouse_select_eval_buffer( - &vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest); + const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits); + basact = mouse_select_eval_buffer(&vc, + buffer, + hits, + vc.view_layer->object_bases.first, + has_bones, + do_nearest, + r_material_slot); } return basact; } +/* mval comes from event->mval, only use within region handlers */ +Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) +{ + return ed_view3d_give_base_under_cursor_ex(C, mval, NULL); +} + Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2]) { Base *base = ED_view3d_give_base_under_cursor(C, mval); @@ -2223,6 +2247,17 @@ Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2]) return NULL; } +struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C, + const int mval[2], + int *r_material_slot) +{ + Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot); + if (base) { + return base->object; + } + return NULL; +} + bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2]) { return ED_view3d_give_object_under_cursor(C, mval) != NULL; @@ -2374,7 +2409,8 @@ static bool ed_object_select_pick(bContext *C, } } else { - basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest); + basact = mouse_select_eval_buffer( + &vc, buffer, hits, startbase, has_bones, do_nearest, NULL); } if (has_bones && basact) { @@ -2436,7 +2472,7 @@ static bool ed_object_select_pick(bContext *C, if (!changed) { /* fallback to regular object selection if no new bundles were selected, * allows to select object parented to reconstruction object */ - basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest); + basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest, NULL); } } } @@ -2677,7 +2713,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) uint buffer[MAXPICKBUF]; const int hits = mixed_bones_object_selectbuffer( - &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true); + &vc, buffer, location, VIEW3D_SELECT_FILTER_NOP, false, true, false); retval = bone_mouse_select_menu(C, buffer, hits, true, extend, deselect, toggle); } if (!retval) { diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 86a610f8dd9..b9f3706b084 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -964,12 +964,13 @@ static bool drw_select_filter_object_mode_lock_for_weight_paint(Object *ob, void * * \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection. */ -int view3d_opengl_select(ViewContext *vc, - uint *buffer, - uint bufsize, - const rcti *input, - eV3DSelectMode select_mode, - eV3DSelectObjectFilter select_filter) +int view3d_opengl_select_ex(ViewContext *vc, + uint *buffer, + uint bufsize, + const rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter, + const bool do_material_slot_selection) { struct bThemeState theme_state; const wmWindowManager *wm = CTX_wm_manager(vc->C); @@ -1119,6 +1120,7 @@ int view3d_opengl_select(ViewContext *vc, use_obedit_skip, draw_surface, use_nearest, + do_material_slot_selection, &rect, drw_select_loop_pass, &drw_select_loop_user_data, @@ -1149,6 +1151,7 @@ int view3d_opengl_select(ViewContext *vc, use_obedit_skip, draw_surface, use_nearest, + do_material_slot_selection, &rect, drw_select_loop_pass, &drw_select_loop_user_data, @@ -1178,6 +1181,16 @@ finally: return hits; } +int view3d_opengl_select(ViewContext *vc, + uint *buffer, + uint bufsize, + const rcti *input, + eV3DSelectMode select_mode, + eV3DSelectObjectFilter select_filter) +{ + return view3d_opengl_select_ex(vc, buffer, bufsize, input, select_mode, select_filter, false); +} + int view3d_opengl_select_with_id_filter(ViewContext *vc, uint *buffer, uint bufsize, -- cgit v1.2.3 From 2b2d427bbac6716092336f0d0f698706a390f4ad Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 8 Sep 2021 09:52:06 +0200 Subject: Fix T90825: Performance texture painting with limited scale. Improve texture painting/uv editing performance when limited scale is active. Cause of the slow down is that the image editor draws the image in maximum resolution, but the 3d viewport uses the limited scale. The variation reuses the same GPU texture and needed to be uploaded/scaled twice to the GPU. This patch will adds texture slots that can hold the scaled down and the maximum resolution image. This would allow better cache hits and reuse of existing caches. Maximum resolution textures are reused for limited scale when they fit to reduce memory and CPU footprint. Reviewed By: fclem Differential Revision: https://developer.blender.org/D12388 --- source/blender/blenkernel/intern/image.c | 67 ++++---- source/blender/blenkernel/intern/image_gpu.c | 222 +++++++++++++++++++-------- source/blender/makesdna/DNA_image_types.h | 28 +++- source/blender/makesrna/intern/rna_image.c | 2 +- 4 files changed, 219 insertions(+), 100 deletions(-) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index d87290e1eb4..966dd0595da 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -155,7 +155,9 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - image_dst->gputexture[i][eye] = NULL; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + image_dst->gputexture[i][eye][slot] = NULL; + } } } @@ -208,9 +210,11 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - key.offset_in_ID = offsetof(Image, gputexture[a][eye]); - key.cache_v = image->gputexture[a][eye]; - function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + key.offset_in_ID = offsetof(Image, gputexture[a][eye][slot]); + key.cache_v = image->gputexture[a][eye]; + function_callback(id, &key, (void **)&image->gputexture[a][eye][slot], 0, user_data); + } } } @@ -239,7 +243,9 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLI_listbase_clear(&ima->gpu_refresh_areas); for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - ima->gputexture[i][j] = NULL; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + ima->gputexture[i][j][slot] = NULL; + } } } @@ -677,8 +683,10 @@ bool BKE_image_has_opengl_texture(Image *ima) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - return true; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + if (ima->gputexture[i][eye][slot] != NULL) { + return true; + } } } } @@ -3531,9 +3539,11 @@ static void image_free_tile(Image *ima, ImageTile *tile) } for (int eye = 0; eye < 2; eye++) { - if (ima->gputexture[i][eye] != NULL) { - GPU_texture_free(ima->gputexture[i][eye]); - ima->gputexture[i][eye] = NULL; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + if (ima->gputexture[i][eye][slot] != NULL) { + GPU_texture_free(ima->gputexture[i][eye][slot]); + ima->gputexture[i][eye][slot] = NULL; + } } } } @@ -3801,14 +3811,16 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] = NULL; + } } } @@ -3863,14 +3875,17 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } for (int eye = 0; eye < 2; eye++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL; + for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] = NULL; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] = NULL; + } } } } diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index d179dd40c33..9712e912bed 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -49,6 +49,7 @@ /* Prototypes. */ static void gpu_free_unused_buffers(void); static void image_free_gpu(Image *ima, const bool immediate); +static void image_free_gpu_limited_scale(Image *ima); static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h); @@ -97,9 +98,11 @@ static int smaller_power_of_2_limit(int num, bool limit_gl_texture_size) return power_of_2_min_i(GPU_texture_size_with_limit(num, limit_gl_texture_size)); } -static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) +static GPUTexture *gpu_texture_create_tile_mapping( + Image *ima, const int multiview_eye, const eImageTextureResolution texture_resolution) { - GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye]; + const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; + GPUTexture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye][resolution]; if (tilearray == NULL) { return 0; @@ -121,13 +124,14 @@ static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multivi } LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { int i = tile->tile_number - 1001; - data[4 * i] = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + data[4 * i] = tile_runtime->tilearray_layer; float *tile_info = &data[4 * width + 4 * i]; - tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; - tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; - tile_info[2] = tile->runtime.tilearray_size[0] / array_w; - tile_info[3] = tile->runtime.tilearray_size[1] / array_h; + tile_info[0] = tile_runtime->tilearray_offset[0] / array_w; + tile_info[1] = tile_runtime->tilearray_offset[1] / array_h; + tile_info[2] = tile_runtime->tilearray_size[0] / array_w; + tile_info[3] = tile_runtime->tilearray_size[1] / array_h; } GPUTexture *tex = GPU_texture_create_1d_array(ima->id.name + 2, width, 2, 1, GPU_RGBA32F, data); @@ -152,9 +156,12 @@ static int compare_packtile(const void *a, const void *b) return tile_a->pack_score < tile_b->pack_score; } -static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) +static GPUTexture *gpu_texture_create_tile_array(Image *ima, + ImBuf *main_ibuf, + const eImageTextureResolution texture_resolution) { - const bool limit_gl_texture_size = (ima->gpuflag & IMA_GPU_MAX_RESOLUTION) == 0; + const bool limit_gl_texture_size = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED; + const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; int arraywidth = 0, arrayheight = 0; ListBase boxes = {NULL}; @@ -200,14 +207,15 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) LISTBASE_FOREACH (PackTile *, packtile, &packed) { ImageTile *tile = packtile->tile; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; tileoffset[0] = packtile->boxpack.x; tileoffset[1] = packtile->boxpack.y; tilesize[0] = packtile->boxpack.w; tilesize[1] = packtile->boxpack.h; - tile->runtime.tilearray_layer = arraylayers; + tile_runtime->tilearray_layer = arraylayers; } BLI_freelistN(&packed); @@ -221,9 +229,10 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) /* Upload each tile one by one. */ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int tilelayer = tile->runtime.tilearray_layer; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int tilelayer = tile_runtime->tilearray_layer; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; if (tilesize[0] == 0 || tilesize[1] == 0) { continue; @@ -268,16 +277,33 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) /** \name Regular gpu texture * \{ */ +static bool image_max_resolution_texture_fits_in_limited_scale(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + BLI_assert_msg(U.glreslimit != 0, + "limited scale function called without limited scale being set."); + GPUTexture *max_resolution_texture = + ima->gputexture[textarget][multiview_eye][IMA_TEXTURE_RESOLUTION_FULL]; + if (max_resolution_texture && GPU_texture_width(max_resolution_texture) <= U.glreslimit && + GPU_texture_height(max_resolution_texture) <= U.glreslimit) { + return true; + } + return false; +} + static GPUTexture **get_image_gpu_texture_ptr(Image *ima, eGPUTextureTarget textarget, - const int multiview_eye) + const int multiview_eye, + const eImageTextureResolution texture_resolution) { const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); BLI_assert(in_range); BLI_assert(multiview_eye == 0 || multiview_eye == 1); + const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; if (in_range) { - return &(ima->gputexture[textarget][multiview_eye]); + return &(ima->gputexture[textarget][multiview_eye][resolution]); } return NULL; } @@ -296,6 +322,21 @@ static GPUTexture *image_gpu_texture_error_create(eGPUTextureTarget textarget) } } +static void image_update_reusable_textures(Image *ima, + eGPUTextureTarget textarget, + const int multiview_eye) +{ + if ((ima->gpuflag & IMA_GPU_HAS_LIMITED_SCALE_TEXTURES) == 0) { + return; + } + + if (ELEM(textarget, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + if (image_max_resolution_texture_fits_in_limited_scale(ima, textarget, multiview_eye)) { + image_free_gpu_limited_scale(ima); + } + } +} + static GPUTexture *image_get_gpu_texture(Image *ima, ImageUser *iuser, ImBuf *ibuf, @@ -315,24 +356,17 @@ static GPUTexture *image_get_gpu_texture(Image *ima, short requested_pass = iuser ? iuser->pass : 0; short requested_layer = iuser ? iuser->layer : 0; short requested_view = iuser ? iuser->multi_index : 0; - const bool limit_resolution = U.glreslimit != 0 && - ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || - (iuser == NULL)); - short requested_gpu_flags = limit_resolution ? 0 : IMA_GPU_MAX_RESOLUTION; -#define GPU_FLAGS_TO_CHECK (IMA_GPU_MAX_RESOLUTION) /* There is room for 2 multiview textures. When a higher number is requested we should always * target the first view slot. This is fine as multi view images aren't used together. */ if (requested_view < 2) { requested_view = 0; } if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer || - ima->gpu_view != requested_view || - ((ima->gpuflag & GPU_FLAGS_TO_CHECK) != requested_gpu_flags)) { + ima->gpu_view != requested_view) { ima->gpu_pass = requested_pass; ima->gpu_layer = requested_layer; ima->gpu_view = requested_view; - ima->gpuflag &= ~GPU_FLAGS_TO_CHECK; - ima->gpuflag |= requested_gpu_flags | IMA_GPU_REFRESH; + ima->gpuflag |= IMA_GPU_REFRESH; } #undef GPU_FLAGS_TO_CHECK @@ -369,7 +403,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (current_view >= 2) { current_view = 0; } - GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view); + const bool limit_resolution = U.glreslimit != 0 && + ((iuser && (iuser->flag & IMA_SHOW_MAX_RESOLUTION) == 0) || + (iuser == NULL)) && + ((ima->gpuflag & IMA_GPU_REUSE_MAX_RESOLUTION) == 0); + const eImageTextureResolution texture_resolution = limit_resolution ? + IMA_TEXTURE_RESOLUTION_LIMITED : + IMA_TEXTURE_RESOLUTION_FULL; + GPUTexture **tex = get_image_gpu_texture_ptr(ima, textarget, current_view, texture_resolution); if (*tex) { return *tex; } @@ -392,22 +433,19 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } if (textarget == TEXTARGET_2D_ARRAY) { - *tex = gpu_texture_create_tile_array(ima, ibuf_intern); + *tex = gpu_texture_create_tile_array(ima, ibuf_intern, texture_resolution); } else if (textarget == TEXTARGET_TILE_MAPPING) { - *tex = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); + *tex = gpu_texture_create_tile_mapping( + ima, iuser ? iuser->multiview_eye : 0, texture_resolution); } else { const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH); const bool store_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(ima, ibuf_intern); - const bool limit_gl_texture_size = (ima->gpuflag & IMA_GPU_MAX_RESOLUTION) == 0; - *tex = IMB_create_gpu_texture(ima->id.name + 2, - ibuf_intern, - use_high_bitdepth, - store_premultiplied, - limit_gl_texture_size); + *tex = IMB_create_gpu_texture( + ima->id.name + 2, ibuf_intern, use_high_bitdepth, store_premultiplied, limit_resolution); if (*tex) { GPU_texture_wrap_mode(*tex, true, false); @@ -425,6 +463,20 @@ static GPUTexture *image_get_gpu_texture(Image *ima, } } + switch (texture_resolution) { + case IMA_TEXTURE_RESOLUTION_LIMITED: + ima->gpuflag |= IMA_GPU_HAS_LIMITED_SCALE_TEXTURES; + break; + + case IMA_TEXTURE_RESOLUTION_FULL: + image_update_reusable_textures(ima, textarget, current_view); + break; + + case IMA_TEXTURE_RESOLUTION_LEN: + BLI_assert_unreachable(); + break; + } + /* if `ibuf` was given, we don't own the `ibuf_intern` */ if (ibuf == NULL) { BKE_image_release_ibuf(ima, ibuf_intern, NULL); @@ -497,22 +549,39 @@ static void image_free_gpu(Image *ima, const bool immediate) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i][eye] != NULL) { - if (immediate) { - GPU_texture_free(ima->gputexture[i][eye]); - } - else { - BLI_mutex_lock(&gpu_texture_queue_mutex); - BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye]); - BLI_mutex_unlock(&gpu_texture_queue_mutex); + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + if (immediate) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + } + else { + BLI_mutex_lock(&gpu_texture_queue_mutex); + BLI_linklist_prepend(&gpu_texture_free_queue, ima->gputexture[i][eye][resolution]); + BLI_mutex_unlock(&gpu_texture_queue_mutex); + } + + ima->gputexture[i][eye][resolution] = NULL; } + } + } + } + + ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); +} - ima->gputexture[i][eye] = NULL; +static void image_free_gpu_limited_scale(Image *ima) +{ + const eImageTextureResolution resolution = IMA_TEXTURE_RESOLUTION_LIMITED; + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + ima->gputexture[i][eye][resolution] = NULL; } } } - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; + ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE | IMA_GPU_HAS_LIMITED_SCALE_TEXTURES); } void BKE_image_free_gputextures(Image *ima) @@ -689,12 +758,21 @@ static void gpu_texture_update_unscaled(GPUTexture *tex, GPU_unpack_row_length_set(0); } -static void gpu_texture_update_from_ibuf( - GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) +static void gpu_texture_update_from_ibuf(GPUTexture *tex, + Image *ima, + ImBuf *ibuf, + ImageTile *tile, + int x, + int y, + int w, + int h, + eImageTextureResolution texture_resolution) { + const int resolution = texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED ? 1 : 0; bool scaled; if (tile != NULL) { - int *tilesize = tile->runtime.tilearray_size; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tilesize = tile_runtime->tilearray_size; scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); } else { @@ -758,9 +836,10 @@ static void gpu_texture_update_from_ibuf( if (scaled) { /* Slower update where we first have to scale the input pixels. */ if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - int tilelayer = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int *tilesize = tile_runtime->tilearray_size; + int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_scaled( tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); } @@ -772,8 +851,9 @@ static void gpu_texture_update_from_ibuf( else { /* Fast update at same resolution. */ if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int tilelayer = tile->runtime.tilearray_layer; + ImageTile_RuntimeTextureSlot *tile_runtime = &tile->runtime.slots[resolution]; + int *tileoffset = tile_runtime->tilearray_offset; + int tilelayer = tile_runtime->tilearray_layer; gpu_texture_update_unscaled( tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset); } @@ -804,16 +884,20 @@ static void gpu_texture_update_from_ibuf( static void image_update_gputexture_ex( Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h) { - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } + const int eye = 0; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][eye][resolution]; + eImageTextureResolution texture_resolution = resolution; + /* Check if we need to update the main gputexture. */ + if (tex != NULL && tile == ima->tiles.first) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h, texture_resolution); + } - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); + /* Check if we need to update the array gputexture. */ + tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]; + if (tex != NULL) { + gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h, texture_resolution); + } } } @@ -917,12 +1001,14 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap) LISTBASE_FOREACH (Image *, ima, &bmain->images) { if (BKE_image_has_opengl_texture(ima)) { if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_mipmap_mode(tex, mipmap, true); + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (ELEM(a, TEXTARGET_2D, TEXTARGET_2D_ARRAY)) { + for (int eye = 0; eye < 2; eye++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *tex = ima->gputexture[a][eye][resolution]; + if (tex != NULL) { + GPU_texture_mipmap_mode(tex, mipmap, true); + } } } } diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 22408687daf..c5bf53aec77 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -94,11 +94,17 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; -typedef struct ImageTile_Runtime { +typedef struct ImageTile_RuntimeTextureSlot { int tilearray_layer; int _pad; int tilearray_offset[2]; int tilearray_size[2]; +} ImageTile_RuntimeTextureSlot; + +typedef struct ImageTile_Runtime { + /* Data per `eImageTextureResolution`. + * Should match `IMA_TEXTURE_RESOLUTION_LEN` */ + ImageTile_RuntimeTextureSlot slots[2]; } ImageTile_Runtime; typedef struct ImageTile { @@ -132,6 +138,15 @@ typedef enum eGPUTextureTarget { TEXTARGET_COUNT, } eGPUTextureTarget; +/* Resolution variations that can be cached for an image. */ +typedef enum eImageTextureResolution { + IMA_TEXTURE_RESOLUTION_FULL = 0, + IMA_TEXTURE_RESOLUTION_LIMITED, + + /* Not an option, but holds the number of options defined for this struct. */ + IMA_TEXTURE_RESOLUTION_LEN +} eImageTextureResolution; + typedef struct Image { ID id; @@ -140,8 +155,8 @@ typedef struct Image { /** Not written in file. */ struct MovieCache *cache; - /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes. */ - struct GPUTexture *gputexture[3][2]; + /** Not written in file 3 = TEXTARGET_COUNT, 2 = stereo eyes, 2 = IMA_TEXTURE_RESOLUTION_LEN. */ + struct GPUTexture *gputexture[3][2][2]; /* sources from: */ ListBase anims; @@ -233,8 +248,11 @@ enum { IMA_GPU_PARTIAL_REFRESH = (1 << 1), /** All mipmap levels in OpenGL texture set? */ IMA_GPU_MIPMAP_COMPLETE = (1 << 2), - /** Current texture resolution won't be limited by the GL Texture Limit user preference. */ - IMA_GPU_MAX_RESOLUTION = (1 << 3), + /* Reuse the max resolution textures as they fit in the limited scale. */ + IMA_GPU_REUSE_MAX_RESOLUTION = (1 << 3), + /* Has any limited scale textures been allocated. + * Adds additional checks to reuse max resolution images when they fit inside limited scale. */ + IMA_GPU_HAS_LIMITED_SCALE_TEXTURES = (1 << 4), }; /* Image.source, where the image comes from */ diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index e44ddb07d53..4a013dc9bd7 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -404,7 +404,7 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) static int rna_Image_bindcode_get(PointerRNA *ptr) { Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0]; + GPUTexture *tex = ima->gputexture[TEXTARGET_2D][0][IMA_TEXTURE_RESOLUTION_FULL]; return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; } -- cgit v1.2.3 From 60e9fb9929c08e705f21fabad9d832ed25e01bd8 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 8 Sep 2021 10:28:45 +0200 Subject: Cleanup: consisten naming slot -> resolution. Makes naming consistent with image_gpu.c --- source/blender/blenkernel/intern/image.c | 54 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 966dd0595da..33f007c6dee 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -155,8 +155,8 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { - image_dst->gputexture[i][eye][slot] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + image_dst->gputexture[i][eye][resolution] = NULL; } } } @@ -210,10 +210,10 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { - key.offset_in_ID = offsetof(Image, gputexture[a][eye][slot]); + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); key.cache_v = image->gputexture[a][eye]; - function_callback(id, &key, (void **)&image->gputexture[a][eye][slot], 0, user_data); + function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); } } } @@ -243,8 +243,8 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLI_listbase_clear(&ima->gpu_refresh_areas); for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { - ima->gputexture[i][j][slot] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + ima->gputexture[i][j][resolution] = NULL; } } } @@ -683,8 +683,8 @@ bool BKE_image_has_opengl_texture(Image *ima) { for (int eye = 0; eye < 2; eye++) { for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { - if (ima->gputexture[i][eye][slot] != NULL) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { return true; } } @@ -3539,10 +3539,10 @@ static void image_free_tile(Image *ima, ImageTile *tile) } for (int eye = 0; eye < 2; eye++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { - if (ima->gputexture[i][eye][slot] != NULL) { - GPU_texture_free(ima->gputexture[i][eye][slot]); - ima->gputexture[i][eye][slot] = NULL; + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + ima->gputexture[i][eye][resolution] = NULL; } } } @@ -3811,15 +3811,15 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } for (int eye = 0; eye < 2; eye++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] = NULL; + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] = NULL; + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; } } } @@ -3875,16 +3875,16 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } for (int eye = 0; eye < 2; eye++) { - for (int slot = 0; slot < IMA_TEXTURE_RESOLUTION_LEN; slot++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][slot] = NULL; + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][slot] = NULL; + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; } } } -- cgit v1.2.3 From b5f2b81751c8d9feae6f5b5fbcef341cdf86f93f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 8 Sep 2021 10:34:21 +0200 Subject: Cleanup: typedef enum for eImageSource. --- source/blender/makesdna/DNA_image_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index c5bf53aec77..630a313e59d 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -256,7 +256,7 @@ enum { }; /* Image.source, where the image comes from */ -enum { +typedef enum eImageSource { /* IMA_SRC_CHECK = 0, */ /* UNUSED */ IMA_SRC_FILE = 1, IMA_SRC_SEQUENCE = 2, @@ -264,7 +264,7 @@ enum { IMA_SRC_GENERATED = 4, IMA_SRC_VIEWER = 5, IMA_SRC_TILED = 6, -}; +} eImageSource; /* Image.type, how to handle or generate the image */ enum { -- cgit v1.2.3 From ba5d9fa27534be3aac0419c71b617a09e3103167 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Wed, 8 Sep 2021 10:35:16 +0200 Subject: Cleanup: typedef enum for eImageType. --- source/blender/makesdna/DNA_image_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 630a313e59d..30ca9540735 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -267,7 +267,7 @@ typedef enum eImageSource { } eImageSource; /* Image.type, how to handle or generate the image */ -enum { +typedef enum eImageType { IMA_TYPE_IMAGE = 0, IMA_TYPE_MULTILAYER = 1, /* generated */ @@ -275,7 +275,7 @@ enum { /* viewers */ IMA_TYPE_R_RESULT = 4, IMA_TYPE_COMPOSITE = 5, -}; +} eImageType; /* Image.gen_type */ enum { -- cgit v1.2.3 From 96ef1843776f22c3a4cee6280740be93ce08615f Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Wed, 8 Sep 2021 09:29:16 -0400 Subject: USD import: remove unused files. Removed unused usd_reader_instance.cc and .h files. --- .../blender/io/usd/intern/usd_reader_instance.cc | 64 ---------------------- source/blender/io/usd/intern/usd_reader_instance.h | 47 ---------------- source/blender/io/usd/intern/usd_reader_stage.cc | 2 +- 3 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 source/blender/io/usd/intern/usd_reader_instance.cc delete mode 100644 source/blender/io/usd/intern/usd_reader_instance.h diff --git a/source/blender/io/usd/intern/usd_reader_instance.cc b/source/blender/io/usd/intern/usd_reader_instance.cc deleted file mode 100644 index e645b0237b9..00000000000 --- a/source/blender/io/usd/intern/usd_reader_instance.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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) 2021 Blender Foundation. - * All rights reserved. - */ - -#include "usd_reader_instance.h" - -#include "BKE_object.h" -#include "DNA_object_types.h" - -#include - -namespace blender::io::usd { - -USDInstanceReader::USDInstanceReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings) - : USDXformReader(prim, import_params, settings) -{ -} - -bool USDInstanceReader::valid() const -{ - return prim_.IsValid() && prim_.IsInstance(); -} - -void USDInstanceReader::create_object(Main *bmain, const double /* motionSampleTime */) -{ - this->object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str()); - this->object_->data = nullptr; - this->object_->transflag |= OB_DUPLICOLLECTION; -} - -void USDInstanceReader::set_instance_collection(Collection *coll) -{ - if (this->object_) { - this->object_->instance_collection = coll; - } -} - -pxr::SdfPath USDInstanceReader::proto_path() const -{ - if (pxr::UsdPrim master = prim_.GetMaster()) { - return master.GetPath(); - } - - return pxr::SdfPath(); -} - -} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_instance.h b/source/blender/io/usd/intern/usd_reader_instance.h deleted file mode 100644 index efc1c69a7dd..00000000000 --- a/source/blender/io/usd/intern/usd_reader_instance.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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) 2021 Blender Foundation. - * All rights reserved. - */ -#pragma once - -#include "usd_reader_xform.h" - -#include - -struct Collection; - -namespace blender::io::usd { - -/* Wraps the UsdGeomXform schema. Creates a Blender Empty object. */ - -class USDInstanceReader : public USDXformReader { - - public: - USDInstanceReader(const pxr::UsdPrim &prim, - const USDImportParams &import_params, - const ImportSettings &settings); - - bool valid() const override; - - void create_object(Main *bmain, double motionSampleTime) override; - - void set_instance_collection(Collection *coll); - - pxr::SdfPath proto_path() const; -}; - -} // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index 233b3d9da4d..8c4cc18a9af 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -20,7 +20,6 @@ #include "usd_reader_stage.h" #include "usd_reader_camera.h" #include "usd_reader_curve.h" -#include "usd_reader_instance.h" #include "usd_reader_light.h" #include "usd_reader_mesh.h" #include "usd_reader_nurbs.h" @@ -34,6 +33,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 6bc6ffc35c58ca4ac1ac1a55367e20fc06f5fe3a Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 8 Sep 2021 15:57:05 +0200 Subject: Fix T91241: wrong labels and identifiers for id sockets `nodeAddSocket` expects the name and identifier in a different order. --- source/blender/nodes/intern/node_socket_declarations.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 418fed146fb..ba9a1870b0c 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -274,7 +274,7 @@ bNodeSocket &build_id_socket(bNodeTree &ntree, StringRefNull identifier) { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, data.idname, name.c_str(), identifier.c_str()); + &ntree, &node, in_out, data.idname, identifier.c_str(), name.c_str()); if (data.hide_label) { socket.flag |= SOCK_HIDE_LABEL; } -- cgit v1.2.3 From 6fc94d18485a57d262a3301ad6255b60abcfd883 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 8 Sep 2021 15:56:50 +0200 Subject: Tests: updates for performance benchmarking * Make "run" command (re-)run all tests, add "update" command to only run queued and outdated tests equivalent to the old "run" command. * Support specifying environment variables for revisions, to easily compare multiple parameter values. * Better sorting of revisions in graph. --- tests/performance/api/config.py | 17 +++++++++++++++-- tests/performance/api/environment.py | 17 +++++++++++++---- tests/performance/api/graph.py | 4 +--- tests/performance/benchmark | 26 ++++++++++++++++++-------- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/tests/performance/api/config.py b/tests/performance/api/config.py index d3a79eede14..aa991e7d7d8 100644 --- a/tests/performance/api/config.py +++ b/tests/performance/api/config.py @@ -25,6 +25,7 @@ class TestEntry: category: str = '' revision: str = '' git_hash: str = '' + environment: Dict = field(default_factory=dict) executable: str = '' date: int = 0 device_type: str = 'CPU' @@ -191,9 +192,10 @@ class TestConfig: # Get entries for specified commits, tags and branches. for revision_name, revision_commit in self.revisions.items(): + revision_commit, environment = self._split_environment_variables(revision_commit) git_hash = env.resolve_git_hash(revision_commit) date = env.git_hash_date(git_hash) - entries += self._get_entries(revision_name, git_hash, '', date) + entries += self._get_entries(revision_name, git_hash, '', environment, date) # Optimization to avoid rebuilds. revisions_to_build = set() @@ -204,6 +206,7 @@ class TestConfig: # Get entries for revisions based on existing builds. for revision_name, executable in self.builds.items(): + executable, environment = self._split_environment_variables(executable) executable_path = env._blender_executable_from_path(pathlib.Path(executable)) if not executable_path: sys.stderr.write(f'Error: build {executable} not found\n') @@ -214,7 +217,7 @@ class TestConfig: env.set_default_blender_executable() mtime = executable_path.stat().st_mtime - entries += self._get_entries(revision_name, git_hash, executable, mtime) + entries += self._get_entries(revision_name, git_hash, executable, environment, mtime) # Detect number of categories for more compact printing. categories = set() @@ -229,6 +232,7 @@ class TestConfig: revision_name: str, git_hash: str, executable: pathlib.Path, + environment: str, date: int) -> None: entries = [] for test in self.tests.tests: @@ -241,10 +245,12 @@ class TestConfig: # Test if revision hash or executable changed. if entry.git_hash != git_hash or \ entry.executable != executable or \ + entry.environment != environment or \ entry.benchmark_type != self.benchmark_type or \ entry.date != date: # Update existing entry. entry.git_hash = git_hash + entry.environment = environment entry.executable = executable entry.benchmark_type = self.benchmark_type entry.date = date @@ -256,6 +262,7 @@ class TestConfig: revision=revision_name, git_hash=git_hash, executable=executable, + environment=environment, date=date, test=test_name, category=test_category, @@ -266,3 +273,9 @@ class TestConfig: entries.append(entry) return entries + + def _split_environment_variables(self, revision): + if isinstance(revision, str): + return revision, {} + else: + return revision[0], revision[1] diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py index 76c731b6118..750d991ebc8 100644 --- a/tests/performance/api/environment.py +++ b/tests/performance/api/environment.py @@ -104,9 +104,10 @@ class TestEnvironment: self._init_default_blender_executable() return True - def set_blender_executable(self, executable_path: pathlib.Path) -> None: + def set_blender_executable(self, executable_path: pathlib.Path, environment: Dict = {}) -> None: # Run all Blender commands with this executable. self.blender_executable = executable_path + self.blender_executable_environment = environment def _blender_executable_name(self) -> pathlib.Path: if platform.system() == "Windows": @@ -150,6 +151,7 @@ class TestEnvironment: def set_default_blender_executable(self) -> None: self.blender_executable = self.default_blender_executable + self.blender_executable_environment = {} def set_log_file(self, filepath: pathlib.Path, clear=True) -> None: # Log all commands and output to this file. @@ -161,7 +163,7 @@ class TestEnvironment: def unset_log_file(self) -> None: self.log_file = None - def call(self, args: List[str], cwd: pathlib.Path, silent=False) -> List[str]: + def call(self, args: List[str], cwd: pathlib.Path, silent: bool=False, environment: Dict={}) -> List[str]: # Execute command with arguments in specified directory, # and return combined stdout and stderr output. @@ -173,7 +175,13 @@ class TestEnvironment: f = open(self.log_file, 'a') f.write('\n' + ' '.join([str(arg) for arg in args]) + '\n\n') - proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + env = os.environ + if len(environment): + env = env.copy() + for key, value in environment.items(): + env[key] = value + + proc = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) # Read line by line lines = [] @@ -208,7 +216,8 @@ class TestEnvironment: else: common_args += ['--background'] - return self.call([self.blender_executable] + common_args + args, cwd=self.base_dir) + return self.call([self.blender_executable] + common_args + args, cwd=self.base_dir, + environment=self.blender_executable_environment) def run_in_blender(self, function: Callable[[Dict], Dict], diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index 4ee5ae7cf0e..fe4d4800894 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -42,7 +42,7 @@ class TestGraph: # Generate one graph for every device x category x result key combination. for category, category_entries in categories.items(): - entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test)) + entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test, entry.date)) outputs = set() for entry in entries: @@ -58,8 +58,6 @@ class TestGraph: self.json = json.dumps(data, indent=2) def chart(self, device_name: str, chart_name: str, entries: List, chart_type: str, output: str) -> Dict: - entries = sorted(entries, key=lambda entry: entry.date) - # Gather used tests. tests = {} for entry in entries: diff --git a/tests/performance/benchmark b/tests/performance/benchmark index ad1e07d0ef3..343af3be7d1 100755 --- a/tests/performance/benchmark +++ b/tests/performance/benchmark @@ -83,15 +83,20 @@ def match_entry(entry: api.TestEntry, args: argparse.Namespace): entry.test.find(args.test) != -1 or \ entry.category.find(args.test) != -1 -def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry: api.TestEntry): +def run_entry(env: api.TestEnvironment, + config: api.TestConfig, + row: List, + entry: api.TestEntry, + update_only: bool): # Check if entry needs to be run. - if entry.status not in ('queued', 'outdated'): + if update_only and entry.status not in ('queued', 'outdated'): print_row(config, row, end='\r') return False # Run test entry. revision = entry.revision git_hash = entry.git_hash + environment = entry.environment testname = entry.test testcategory = entry.category device_type = entry.device_type @@ -116,13 +121,15 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry print_row(config, row, end='\r') executable_ok = True if len(entry.executable): - env.set_blender_executable(pathlib.Path(entry.executable)) + env.set_blender_executable(pathlib.Path(entry.executable), environment) else: env.checkout(git_hash) executable_ok = env.build() if not executable_ok: entry.status = 'failed' entry.error_msg = 'Failed to build' + else: + env.set_blender_executable(env.blender_executable, environment) # Run test and update output and status. if executable_ok: @@ -219,7 +226,7 @@ def cmd_reset(env: api.TestEnvironment, argv: List): config.queue.write() -def cmd_run(env: api.TestEnvironment, argv: List): +def cmd_run(env: api.TestEnvironment, argv: List, update_only: bool): # Run tests. parser = argparse.ArgumentParser() parser.add_argument('config', nargs='?', default=None) @@ -233,7 +240,7 @@ def cmd_run(env: api.TestEnvironment, argv: List): for row in config.queue.rows(use_revision_columns(config)): if match_entry(row[0], args): for entry in row: - if run_entry(env, config, row, entry): + if run_entry(env, config, row, entry, update_only): updated = True # Write queue every time in case running gets interrupted, # so it can be resumed. @@ -268,8 +275,9 @@ def main(): ' \n' ' list List available tests, devices and configurations\n' ' \n' - ' run [] [] Execute tests for configuration\n' - ' reset [] [] Clear tests results from config, for re-running\n' + ' run [] [] Execute all tests in configuration\n' + ' update [] [] Execute only queued and outdated tests\n' + ' reset [] [] Clear tests results in configuration\n' ' status [] [] List configurations and their tests\n' ' \n' ' graph a.json b.json... -o out.html Create graph from results in JSON files\n') @@ -304,7 +312,9 @@ def main(): if args.command == 'list': cmd_list(env, argv) elif args.command == 'run': - cmd_run(env, argv) + cmd_run(env, argv, update_only=False) + elif args.command == 'update': + cmd_run(env, argv, update_only=True) elif args.command == 'reset': cmd_reset(env, argv) elif args.command == 'status': -- cgit v1.2.3 From 5bfc3a342170f961a5474687434e57b69b5ece2d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 8 Sep 2021 19:56:07 +0200 Subject: Fix error running benchmark script with environment variables for builds Ref D12434 --- tests/performance/api/config.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/performance/api/config.py b/tests/performance/api/config.py index aa991e7d7d8..b5e2b390aa3 100644 --- a/tests/performance/api/config.py +++ b/tests/performance/api/config.py @@ -161,7 +161,13 @@ class TestConfig: def read_blender_executables(env, name) -> List: config = TestConfig._read_config_module(env.base_dir / name) builds = getattr(config, 'builds', {}) - return [pathlib.Path(build) for build in builds.values()] + executables = [] + + for executable in builds.values(): + executable, _ = TestConfig._split_environment_variables(executable) + executables.append(pathlib.Path(executable)) + + return executables @staticmethod def _read_config_module(base_dir: pathlib.Path) -> None: @@ -274,7 +280,8 @@ class TestConfig: return entries - def _split_environment_variables(self, revision): + @staticmethod + def _split_environment_variables(revision): if isinstance(revision, str): return revision, {} else: -- cgit v1.2.3 From 8f785524ae6309c2bb949537b33146d67d69a149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Thu, 19 Aug 2021 22:21:22 +0200 Subject: VSE: Adding a panning angle for multichannel audio. The panning angle allows a more intuitive panning when the output is surround sound. It sets the angle on the horizontal plane around the listener. 0 degrees is to the front, negative values go to the left and positive ones to the right. +/-180 degrees is directly from the back. Technical detail: the panning value is linear with the panning angle with a factor of 90 degrees. For stereo this means that -1 is left and +1 right, since the speakers are exactly 90 degrees to either side. Differential Revision: https://developer.blender.org/D12275 --- release/scripts/startup/bl_ui/space_sequencer.py | 43 +++++++++++++++++------- source/blender/makesrna/intern/rna_sequencer.c | 15 ++++++++- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 30115618f3d..be669b1fe86 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1659,7 +1659,7 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): def draw(self, context): layout = self.layout - layout.use_property_split = True + layout.use_property_split = False st = context.space_data strip = context.active_sequence_strip @@ -1667,20 +1667,39 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): layout.active = not strip.mute - col = layout.column() - - col.prop(strip, "volume", text="Volume") - col.prop(strip, "pitch") - - col = layout.column() - col.prop(strip, "pan") - col.enabled = sound is not None and sound.use_mono - if sound is not None: col = layout.column() + + split = col.split(factor=0.4) + split.label(text="") + split.prop(sound, "use_mono") if st.waveform_display_type == 'DEFAULT_WAVEFORMS': - col.prop(strip, "show_waveform") - col.prop(sound, "use_mono") + split = col.split(factor=0.4) + split.label(text="") + split.prop(strip, "show_waveform") + + col = layout.column() + + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Volume") + split.prop(strip, "volume", text="") + + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pitch") + split.prop(strip, "pitch", text="") + + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pan") + audio_channels = context.scene.render.ffmpeg.audio_channels + pan_text = "" + if audio_channels != 'MONO' and audio_channels != 'STEREO': + pan_text = "%.2f°" % (strip.pan * 90) + split.prop(strip, "pan", text=pan_text) + split.enabled = sound.use_mono and audio_channels != 'MONO' + class SEQUENCER_PT_adjust_comp(SequencerButtonsPanel, Panel): diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index d9837f21833..cd87e4d10c1 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -845,6 +845,17 @@ static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, Pointer DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); } +static void rna_Sequence_pan_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + Scene *scene = (Scene *)ptr->owner_id; + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmax = 1 + (int)(scene->r.ffcodecdata.audio_channels > 2); + *softmin = -*softmax; +} + static int rna_Sequence_input_count_get(PointerRNA *ptr) { Sequence *seq = (Sequence *)(ptr->data); @@ -2559,8 +2570,10 @@ static void rna_def_sound(BlenderRNA *brna) prop = RNA_def_property(srna, "pan", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "pan"); - RNA_def_property_range(prop, -2.0f, 2.0f); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -2, 2, 1, 2); RNA_def_property_ui_text(prop, "Pan", "Playback panning of the sound (only for Mono sources)"); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_Sequence_pan_range"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_audio_update"); prop = RNA_def_property(srna, "show_waveform", PROP_BOOLEAN, PROP_NONE); -- cgit v1.2.3 From 4e91cd5c11fa2cbadfdaad8c527a63fed8ae4951 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 8 Sep 2021 15:46:02 -0500 Subject: Fix T91255: IDProperty UI as_dict() returns step as default value Another typo in this section of code. --- source/blender/python/generic/idprop_py_ui_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c index 42856d88472..7827bd48dfe 100644 --- a/source/blender/python/generic/idprop_py_ui_api.c +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -468,7 +468,7 @@ static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict) Py_DECREF(list); } else { - PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step)); + PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->default_value)); Py_DECREF(item); } } @@ -499,7 +499,7 @@ static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict) Py_DECREF(list); } else { - PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step)); + PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->default_value)); Py_DECREF(item); } } -- cgit v1.2.3 From a131e3bec7f8cf9b81a110881030d983667ce826 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Wed, 8 Sep 2021 20:25:52 -0300 Subject: Fix GPU Buffer not allowing uint values greater than one byte Error in format passed in `PyArg_Parse` --- source/blender/python/gpu/gpu_py_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index 0fef59d6352..abfde7b48c8 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -485,7 +485,7 @@ static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, int i, PyObject *v) case GPU_DATA_UINT: case GPU_DATA_UINT_24_8: case GPU_DATA_10_11_11_REV: - return PyArg_Parse(v, "b:Expected ints", &self->buf.as_uint[i]) ? 0 : -1; + return PyArg_Parse(v, "I:Expected unsigned ints", &self->buf.as_uint[i]) ? 0 : -1; default: return 0; /* should never happen */ } -- cgit v1.2.3 From df65103bf0018439409eb3f646793f9b95c01d13 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 8 Sep 2021 18:44:35 -0500 Subject: Fix: Incorrect default for exposed geometry nodes vectors --- source/blender/modifiers/intern/MOD_nodes.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 9d8630b21e7..47fb6826ca3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -329,8 +329,8 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) ui_data->max = ui_data->soft_max = (double)value->max; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default"); ui_data->default_array_len = 3; - for (int i = 3; i < 3; i++) { - ui_data->default_array[i] = (double)value->value[i]; + for (const int i : IndexRange(3)) { + ui_data->default_array[i] = double(value->value[i]); } return property; } -- cgit v1.2.3 From f9ebd17b4b124d93e4ad94c2c1a2ce020d8d334b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Sep 2021 13:02:16 +1000 Subject: Gizmo: warn when 2D projection fails with non-invertable matrices Add a warning to quickly pinpoint the problem. This would have simplified tracking down this problem in D12105. --- .../blender/editors/gizmo_library/CMakeLists.txt | 1 + .../editors/gizmo_library/gizmo_library_utils.c | 33 +++++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/gizmo_library/CMakeLists.txt b/source/blender/editors/gizmo_library/CMakeLists.txt index eeb1e60166b..bfe8334b390 100644 --- a/source/blender/editors/gizmo_library/CMakeLists.txt +++ b/source/blender/editors/gizmo_library/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/clog ../../../../intern/eigen ../../../../intern/glew-mx ../../../../intern/guardedalloc diff --git a/source/blender/editors/gizmo_library/gizmo_library_utils.c b/source/blender/editors/gizmo_library/gizmo_library_utils.c index 7d0ae5afb9b..a0db2a8e627 100644 --- a/source/blender/editors/gizmo_library/gizmo_library_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_library_utils.c @@ -39,9 +39,13 @@ #include "ED_view3d.h" +#include "CLG_log.h" + /* own includes */ #include "gizmo_library_intern.h" +static CLG_LogRef LOG = {"ed.gizmo.library_utils"}; + /* factor for precision tweaking */ #define GIZMO_PRECISION_FAC 0.05f @@ -182,7 +186,7 @@ bool gizmo_window_project_2d(bContext *C, bool use_offset, float r_co[2]) { - float mat[4][4]; + float mat[4][4], imat[4][4]; { float mat_identity[4][4]; struct WM_GizmoMatrixParams params = {NULL}; @@ -193,6 +197,14 @@ bool gizmo_window_project_2d(bContext *C, WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat); } + if (!invert_m4_m4(imat, mat)) { + CLOG_WARN(&LOG, + "Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted " + "(projection will fail)", + gz->type->idname, + gz->parent_gzgroup->type->idname); + } + /* rotate mouse in relation to the center and relocate it */ if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { /* For 3d views, transform 2D mouse pos onto plane. */ @@ -202,8 +214,6 @@ bool gizmo_window_project_2d(bContext *C, plane_from_point_normal_v3(plane, mat[3], mat[2]); bool clip_ray = ((RegionView3D *)region->regiondata)->is_persp; if (ED_view3d_win_to_3d_on_plane(region, plane, mval, clip_ray, co)) { - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); r_co[0] = co[(axis + 1) % 3]; r_co[1] = co[(axis + 2) % 3]; @@ -213,8 +223,6 @@ bool gizmo_window_project_2d(bContext *C, } float co[3] = {mval[0], mval[1], 0.0f}; - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); copy_v2_v2(r_co, co); return true; @@ -223,7 +231,7 @@ bool gizmo_window_project_2d(bContext *C, bool gizmo_window_project_3d( bContext *C, const struct wmGizmo *gz, const float mval[2], bool use_offset, float r_co[3]) { - float mat[4][4]; + float mat[4][4], imat[4][4]; { float mat_identity[4][4]; struct WM_GizmoMatrixParams params = {NULL}; @@ -234,20 +242,25 @@ bool gizmo_window_project_3d( WM_gizmo_calc_matrix_final_params(gz, ¶ms, mat); } + if (!invert_m4_m4(imat, mat)) { + CLOG_WARN(&LOG, + "Gizmo \"%s\" of group \"%s\" has matrix that could not be inverted " + "(projection will fail)", + gz->type->idname, + gz->parent_gzgroup->type->idname); + } + if (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) { View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); /* NOTE: we might want a custom reference point passed in, * instead of the gizmo center. */ ED_view3d_win_to_3d(v3d, region, mat[3], mval, r_co); - invert_m4(mat); - mul_m4_v3(mat, r_co); + mul_m4_v3(imat, r_co); return true; } float co[3] = {mval[0], mval[1], 0.0f}; - float imat[4][4]; - invert_m4_m4(imat, mat); mul_m4_v3(imat, co); copy_v2_v2(r_co, co); return true; -- cgit v1.2.3 From c8f80453d5c8e7004b46b866ca9923ce59766c7b Mon Sep 17 00:00:00 2001 From: Maxime Casas Date: Thu, 9 Sep 2021 14:01:15 +1000 Subject: Modifier: add support for vertex groups Allow blending the imported cache with the modifiers stack above the MeshCache modifier. This is particularly useful for instance when dealing with cloth simulations performed in another software, where some parts of the cloth are completely pinned (non-simulated, following the armature). Indeed, this would allow modifying the animation in some areas without having to rebake the other parts or the cloth, resulting in a much more flexible workflow. Reviewed By: #modeling, campbellbarton, mont29 Ref D9898 --- source/blender/makesdna/DNA_modifier_types.h | 6 ++ source/blender/makesrna/intern/rna_modifier.c | 15 +++++ source/blender/modifiers/intern/MOD_meshcache.c | 73 ++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 13213f70fed..8520786a030 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1942,6 +1942,7 @@ typedef struct MeshCacheModifierData { float factor; char deform_mode; + char defgrp_name[64]; char _pad[7]; /* play_mode == MOD_MESHCACHE_PLAY_CFEA */ @@ -1958,6 +1959,11 @@ typedef struct MeshCacheModifierData { char filepath[1024]; } MeshCacheModifierData; +/* MeshCache modifier flags. */ +enum { + MOD_MESHCACHE_INVERT_VERTEX_GROUP = 1 << 0, +}; + enum { MOD_MESHCACHE_TYPE_MDD = 1, MOD_MESHCACHE_TYPE_PC2 = 2, diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index f11d845c582..fb4fd4528d6 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -755,6 +755,7 @@ RNA_MOD_VGROUP_NAME_SET(LaplacianDeform, anchor_grp_name); RNA_MOD_VGROUP_NAME_SET(LaplacianSmooth, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Lattice, name); RNA_MOD_VGROUP_NAME_SET(Mask, vgroup); +RNA_MOD_VGROUP_NAME_SET(MeshCache, defgrp_name); RNA_MOD_VGROUP_NAME_SET(MeshDeform, defgrp_name); RNA_MOD_VGROUP_NAME_SET(NormalEdit, defgrp_name); RNA_MOD_VGROUP_NAME_SET(Shrinkwrap, vgroup_name); @@ -6045,6 +6046,20 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Influence", "Influence of the deformation"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "defgrp_name"); + RNA_def_property_ui_text( + prop, + "Vertex Group", + "Name of the Vertex Group which determines the influence of the modifier per point"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_MeshCacheModifier_defgrp_name_set"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_MESHCACHE_INVERT_VERTEX_GROUP); + RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* -------------------------------------------------------------------- */ /* Axis Conversion */ prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6ef64ad8bc9..74f9887a973 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -36,8 +36,11 @@ #include "DNA_screen_types.h" #include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_wrapper.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -53,6 +56,7 @@ #include "MOD_meshcache_util.h" /* utility functions */ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" +#include "MOD_util.h" static void initData(ModifierData *md) { @@ -84,11 +88,16 @@ static bool isDisabled(const struct Scene *UNUSED(scene), static void meshcache_do(MeshCacheModifierData *mcmd, Scene *scene, Object *ob, + Mesh *mesh, float (*vertexCos_Real)[3], int numVerts) { const bool use_factor = mcmd->factor < 1.0f; - float(*vertexCos_Store)[3] = (use_factor || + int influence_group_index; + MDeformVert *dvert; + MOD_get_vgroup(ob, mesh, mcmd->defgrp_name, &dvert, &influence_group_index); + + float(*vertexCos_Store)[3] = (use_factor || influence_group_index != -1 || (mcmd->deform_mode == MOD_MESHCACHE_DEFORM_INTEGRATE)) ? MEM_malloc_arrayN( numVerts, sizeof(*vertexCos_Store), __func__) : @@ -256,7 +265,29 @@ static void meshcache_do(MeshCacheModifierData *mcmd, if (vertexCos_Store) { if (ok) { - if (use_factor) { + if (influence_group_index != -1) { + const float global_factor = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ? + -mcmd->factor : + mcmd->factor; + const float global_offset = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ? + mcmd->factor : + 0.0f; + if (mesh->dvert != NULL) { + for (int i = 0; i < numVerts; i++) { + /* For each vertex, compute its blending factor between the mesh cache (for `fac = 0`) + * and the former position of the vertex (for `fac = 1`). */ + const MDeformVert *currentIndexDVert = dvert + i; + const float local_vertex_fac = global_offset + + BKE_defvert_find_weight(currentIndexDVert, + influence_group_index) * + global_factor; + interp_v3_v3v3( + vertexCos_Real[i], vertexCos_Real[i], vertexCos_Store[i], local_vertex_fac); + } + } + } + else if (use_factor) { + /* Influence_group_index is -1. */ interp_vn_vn(*vertexCos_Real, *vertexCos_Store, mcmd->factor, numVerts * 3); } else { @@ -270,34 +301,59 @@ static void meshcache_do(MeshCacheModifierData *mcmd, static void deformVerts(ModifierData *md, const ModifierEvalContext *ctx, - Mesh *UNUSED(mesh), + Mesh *mesh, float (*vertexCos)[3], int numVerts) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts); + Mesh *mesh_src = NULL; + + if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { + /* `mesh_src` is only needed for vertex groups. */ + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, numVerts, false, false); + } + meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts); + + if (!ELEM(mesh_src, NULL, mesh)) { + BKE_id_free(NULL, mesh_src); + } } static void deformVertsEM(ModifierData *md, const ModifierEvalContext *ctx, - struct BMEditMesh *UNUSED(editData), - Mesh *UNUSED(mesh), + struct BMEditMesh *editData, + Mesh *mesh, float (*vertexCos)[3], int numVerts) { MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); - meshcache_do(mcmd, scene, ctx->object, vertexCos, numVerts); + Mesh *mesh_src = NULL; + + if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { + /* `mesh_src` is only needed for vertex groups. */ + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, numVerts, false, false); + } + if (mesh_src != NULL) { + BKE_mesh_wrapper_ensure_mdata(mesh_src); + } + + meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, numVerts); + + if (!ELEM(mesh_src, NULL, mesh)) { + BKE_id_free(NULL, mesh_src); + } } static void panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayoutSetPropSep(layout, true); @@ -307,6 +363,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "factor", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(layout, ptr, "deform_mode", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE); + modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); modifier_panel_end(layout, ptr); } -- cgit v1.2.3 From bda9e4238a07a72c26598ea9025e00f6ca896750 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Sep 2021 14:24:22 +1000 Subject: Fix smooth-view failure to add mouse-move events View operations that left the cursor over a gizmo were not being updated because the mouse-move event was added while the view was animated instead of once the animation had completed. Mouse-move events were also missing when smooth-view was disabled. This fixes a glitch with the 3D view navigation gizmo where multiple clicks on the view aligned axis failed to switch to the opposite side unless the user moved the cursor between clicks. --- source/blender/editors/space_view3d/view3d_view.c | 86 +++++++++++------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index b9f3706b084..a6dd95cac04 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -137,7 +137,6 @@ void ED_view3d_smooth_view_ex( { RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore sms = {{0}}; - bool ok = false; /* initialize sms */ view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d); @@ -200,29 +199,30 @@ void ED_view3d_smooth_view_ex( sms.to_camera = true; /* restore view3d values in end */ } - /* skip smooth viewing for external render engine draw */ - if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { - bool changed = false; /* zero means no difference */ + bool changed = false; /* zero means no difference */ - if (sview->camera_old != sview->camera) { - changed = true; - } - else if (sms.dst.dist != rv3d->dist) { - changed = true; - } - else if (sms.dst.lens != v3d->lens) { - changed = true; - } - else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) { - changed = true; - } - else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) { - changed = true; - } + if (sview->camera_old != sview->camera) { + changed = true; + } + else if (sms.dst.dist != rv3d->dist) { + changed = true; + } + else if (sms.dst.lens != v3d->lens) { + changed = true; + } + else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) { + changed = true; + } + else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) { + changed = true; + } + + /* The new view is different from the previous state. */ + if (changed) { + + /* Skip smooth viewing for external render engine draw. */ + if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { - /* The new view is different from the old one - * so animate the view */ - if (changed) { /* original values */ if (sview->camera_old) { Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); @@ -279,27 +279,25 @@ void ED_view3d_smooth_view_ex( } /* #TIMER1 is hard-coded in key-map. */ rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); - - ok = true; } - } + else { + if (sms.to_camera == false) { + copy_v3_v3(rv3d->ofs, sms.dst.ofs); + copy_qt_qt(rv3d->viewquat, sms.dst.quat); + rv3d->dist = sms.dst.dist; + v3d->lens = sms.dst.lens; - /* if we get here nothing happens */ - if (ok == false) { - if (sms.to_camera == false) { - copy_v3_v3(rv3d->ofs, sms.dst.ofs); - copy_qt_qt(rv3d->viewquat, sms.dst.quat); - rv3d->dist = sms.dst.dist; - v3d->lens = sms.dst.lens; + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + } - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - } + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_copy(area, region); + } - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_copy(area, region); - } + ED_region_tag_redraw(region); - ED_region_tag_redraw(region); + WM_event_add_mousemove(win); + } } } @@ -320,6 +318,7 @@ void ED_view3d_smooth_view(bContext *C, /* only meant for timer usage */ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) { + wmWindowManager *wm = CTX_wm_manager(C); RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore *sms = rv3d->sms; float step, step_inv; @@ -333,6 +332,7 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b /* end timer */ if (step >= 1.0f) { + wmWindow *win = CTX_wm_window(C); /* if we went to camera, store the original */ if (sms->to_camera) { @@ -355,9 +355,12 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b MEM_freeN(rv3d->sms); rv3d->sms = NULL; - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rv3d->smooth_timer); + WM_event_remove_timer(wm, win, rv3d->smooth_timer); rv3d->smooth_timer = NULL; rv3d->rflag &= ~RV3D_NAVIGATING; + + /* Event handling won't know if a UI item has been moved under the pointer. */ + WM_event_add_mousemove(win); } else { /* ease in/out */ @@ -380,12 +383,9 @@ static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, b const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (ED_screen_animation_playing(CTX_wm_manager(C))) { + if (ED_screen_animation_playing(wm)) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } - - /* Event handling won't know if a UI item has been moved under the pointer. */ - WM_event_add_mousemove(CTX_wm_window(C)); } if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { -- cgit v1.2.3 From 3da09f4e2992bcd3f6d3f26489ed18c53cd1cc22 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Sep 2021 16:26:15 +1000 Subject: Cleanup: remove newlines from logging text Line endings are already added. --- source/blender/io/alembic/exporter/abc_writer_abstract.cc | 2 +- source/blender/makesrna/intern/rna_access_compare_override.c | 2 +- source/blender/python/intern/bpy_interface.c | 2 +- source/blender/windowmanager/xr/intern/wm_xr_session.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/io/alembic/exporter/abc_writer_abstract.cc b/source/blender/io/alembic/exporter/abc_writer_abstract.cc index 910e04f3bf5..3665494c046 100644 --- a/source/blender/io/alembic/exporter/abc_writer_abstract.cc +++ b/source/blender/io/alembic/exporter/abc_writer_abstract.cc @@ -118,7 +118,7 @@ void ABCAbstractWriter::update_bounding_box(Object *object) if (!bb) { if (object->type != OB_CAMERA) { - CLOG_WARN(&LOG, "Bounding box is null!\n"); + CLOG_WARN(&LOG, "Bounding box is null!"); } bounding_box_.min.x = bounding_box_.min.y = bounding_box_.min.z = 0; bounding_box_.max.x = bounding_box_.max.y = bounding_box_.max.z = 0; diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 2c552970c82..f8a36c1b2e6 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -788,7 +788,7 @@ bool RNA_struct_override_matches(Main *bmain, continue; } - CLOG_INFO(&LOG, 5, "Override Checking %s\n", rna_path); + CLOG_INFO(&LOG, 5, "Override Checking %s", rna_path); IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path); if (ignore_overridden && op != NULL) { diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 68731a91dc9..7a93a076621 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -753,7 +753,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not a valid type", member); } else { - CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found\n", member); + CLOG_INFO(BPY_LOG_CONTEXT, 1, "'%s' not found", member); } } else { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index dc15b579e9d..88bf3ff453c 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -705,7 +705,7 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, } if (failure) { - CLOG_ERROR(&LOG, "Failed to get buffer, %s\n", err_out); + CLOG_ERROR(&LOG, "Failed to get buffer, %s", err_out); return false; } -- cgit v1.2.3 From b81364837882d0574fbc97e6399300d669e9015e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Sep 2021 16:27:52 +1000 Subject: Fix typo checking empty gizmo keymap --- source/blender/windowmanager/intern/wm_event_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 1f47b152a2b..83a9a6c6383 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3749,7 +3749,7 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, const char *keymap_id = NULL; /* Support for the gizmo owning the tool keymap. */ - if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\n') { + if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\0') { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { -- cgit v1.2.3 From 9bb99532a5fd2bea0d31baeced47b92b36649109 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 8 Sep 2021 16:50:21 +0200 Subject: Fix typo in BKE_object_as_kdtree Seems like an oversight in {rB86635402d516}? Stumbled over this while investigating another report, but this line in its current form does not make sense (was taking derivedFinal - not derivedDeform - prior so I assume this has to be BKE_object_get_evaluated_mesh now). (it is now only used for vertex parenting where this should not be an issue, but best keep this generic). Differential Revision: https://developer.blender.org/D12425 --- source/blender/blenkernel/intern/object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index d0d1db9b4f8..aea8d84047d 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -5312,7 +5312,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) unsigned int i; Mesh *me_eval = ob->runtime.mesh_deform_eval ? ob->runtime.mesh_deform_eval : - ob->runtime.mesh_deform_eval; + BKE_object_get_evaluated_mesh(ob); const int *index; if (me_eval && (index = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) { -- cgit v1.2.3 From f8ead736a017a013777bd9e20a76b461bd990dad Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 9 Sep 2021 09:16:03 +0200 Subject: Fix FONT objects cannot use Object Font anymore Mistake in {rB459974896228}. To use Object Fonts, (vertex) instancing needs to be enabled. So bring back the instancing panel and improve the instancing choice (similar to rB6c0c766bcaa0) by just giving the 'Vertex' choice (or 'None') and explain this is only used for Object Fonts on characters. Was reported in D11348 itself. Differential Revision: https://developer.blender.org/D12438 --- release/scripts/startup/bl_ui/properties_object.py | 3 ++- source/blender/makesrna/intern/rna_object.c | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py index 52af4fafd09..81a641a20cf 100644 --- a/release/scripts/startup/bl_ui/properties_object.py +++ b/release/scripts/startup/bl_ui/properties_object.py @@ -267,7 +267,8 @@ class OBJECT_PT_instancing(ObjectButtonsPanel, Panel): @classmethod def poll(cls, context): ob = context.object - return (ob.type in {'MESH', 'EMPTY', 'POINTCLOUD'}) + # FONT objects need (vertex) instancing for the 'Object Font' feature + return (ob.type in {'MESH', 'EMPTY', 'POINTCLOUD', 'FONT'}) def draw(self, context): layout = self.layout diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index d3cd3158db1..99865078cbe 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -224,6 +224,12 @@ static EnumPropertyItem instance_items_empty[] = { INSTANCE_ITEM_COLLECTION, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem instance_items_font[] = { + {0, "NONE", 0, "None", ""}, + {OB_DUPLIVERTS, "VERTS", 0, "Vertices", "Use Object Font on characters"}, + {0, NULL, 0, NULL, NULL}, +}; #endif #undef INSTANCE_ITEMS_SHARED #undef INSTANCE_ITEM_COLLECTION @@ -762,6 +768,9 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C else if (ob->type == OB_POINTCLOUD) { item = instance_items_pointcloud; } + else if (ob->type == OB_FONT) { + item = instance_items_font; + } else { item = instance_items_nogroup; } -- cgit v1.2.3 From 0c0e5a84204bda817c1e77a82cc7099aca4cf5be Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 9 Sep 2021 10:48:26 +0200 Subject: IDmanagement: makelocal: Fix mistake in recent commit. rB8cc3d2d6f51f introduced option to force make_local code to either copy or actually make a linked ID local, but logic of boolean options handling was broken. This commit simplifies logic here and fixes the issue. NOTE: Since those new options were not used yet this was a harmless bug. --- source/blender/blenkernel/intern/brush.c | 40 ++++++++++------- source/blender/blenkernel/intern/lib_id.c | 74 +++++++++++++++++-------------- source/blender/blenkernel/intern/object.c | 48 +++++++++++--------- 3 files changed, 90 insertions(+), 72 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 56c22df3b02..50264f348e9 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -148,8 +148,8 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) Brush *brush = (Brush *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; BLI_assert(force_copy == false || force_copy != force_local); bool is_local = false, is_lib = false; @@ -166,27 +166,33 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) if (!force_local && !force_copy) { BKE_library_ID_test_usages(bmain, brush, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - if (lib_local || is_local || force_copy || force_local) { - if (!is_lib || force_local) { - BKE_lib_id_clear_library_data(bmain, &brush->id); - BKE_lib_id_expand_local(bmain, &brush->id); + if (force_local) { + BKE_lib_id_clear_library_data(bmain, &brush->id); + BKE_lib_id_expand_local(bmain, &brush->id); - /* enable fake user by default */ - id_fake_user_set(&brush->id); - } - else { - Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */ + /* enable fake user by default */ + id_fake_user_set(&brush->id); + } + else if (force_copy) { + Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */ - brush_new->id.us = 0; + brush_new->id.us = 0; - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(brush, brush_new); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(brush, brush_new); - if (!lib_local) { - BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } + if (!lib_local) { + BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE); } } } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index cd0c3635dac..66b83272769 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -411,8 +411,8 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) } const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; - const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; BLI_assert(force_copy == false || force_copy != force_local); bool is_local = false, is_lib = false; @@ -426,43 +426,49 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) if (!force_copy && !force_local) { BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - if (lib_local || is_local || force_copy || force_local) { - if (!is_lib || force_local) { - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); - } - else { - ID *id_new = BKE_id_copy(bmain, id); - - /* Should not fail in expected use cases, - * but a few ID types cannot be copied (LIB, WM, SCR...). */ - if (id_new != NULL) { - id_new->us = 0; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(id, id_new); - Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); - if (key && key_new) { - ID_NEW_SET(key, key_new); - } - bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); - if (ntree && ntree_new) { - ID_NEW_SET(ntree, ntree_new); - } - if (GS(id->name) == ID_SCE) { - Collection *master_collection = ((Scene *)id)->master_collection, - *master_collection_new = ((Scene *)id_new)->master_collection; - if (master_collection && master_collection_new) { - ID_NEW_SET(master_collection, master_collection_new); - } - } + if (force_local) { + BKE_lib_id_clear_library_data(bmain, id); + BKE_lib_id_expand_local(bmain, id); + } + else if (force_copy) { + ID *id_new = BKE_id_copy(bmain, id); + + /* Should not fail in expected use cases, + * but a few ID types cannot be copied (LIB, WM, SCR...). */ + if (id_new != NULL) { + id_new->us = 0; - if (!lib_local) { - BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(id, id_new); + Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); + if (key && key_new) { + ID_NEW_SET(key, key_new); + } + bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); + if (ntree && ntree_new) { + ID_NEW_SET(ntree, ntree_new); + } + if (GS(id->name) == ID_SCE) { + Collection *master_collection = ((Scene *)id)->master_collection, + *master_collection_new = ((Scene *)id_new)->master_collection; + if (master_collection && master_collection_new) { + ID_NEW_SET(master_collection, master_collection_new); } } + + if (!lib_local) { + BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); + } } } } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index aea8d84047d..b4343578eab 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -331,8 +331,8 @@ static void object_make_local(Main *bmain, ID *id, const int flags) Object *ob = (Object *)id; const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; const bool clear_proxy = (flags & LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING) == 0; - const bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; - const bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; + bool force_local = (flags & LIB_ID_MAKELOCAL_FORCE_LOCAL) != 0; + bool force_copy = (flags & LIB_ID_MAKELOCAL_FORCE_COPY) != 0; BLI_assert(force_copy == false || force_copy != force_local); bool is_local = false, is_lib = false; @@ -346,32 +346,38 @@ static void object_make_local(Main *bmain, ID *id, const int flags) if (!force_local && !force_copy) { BKE_library_ID_test_usages(bmain, ob, &is_local, &is_lib); + if (lib_local || is_local) { + if (!is_lib) { + force_local = true; + } + else { + force_copy = true; + } + } } - if (lib_local || is_local || force_copy || force_local) { - if (!is_lib || force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id); - BKE_lib_id_expand_local(bmain, &ob->id); - if (clear_proxy) { - if (ob->proxy_from != NULL) { - ob->proxy_from->proxy = NULL; - ob->proxy_from->proxy_group = NULL; - } - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + if (force_local) { + BKE_lib_id_clear_library_data(bmain, &ob->id); + BKE_lib_id_expand_local(bmain, &ob->id); + if (clear_proxy) { + if (ob->proxy_from != NULL) { + ob->proxy_from->proxy = NULL; + ob->proxy_from->proxy_group = NULL; } + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; } - else { - Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); - id_us_min(&ob_new->id); + } + else if (force_copy) { + Object *ob_new = (Object *)BKE_id_copy(bmain, &ob->id); + id_us_min(&ob_new->id); - ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL; + ob_new->proxy = ob_new->proxy_from = ob_new->proxy_group = NULL; - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(ob, ob_new); + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(ob, ob_new); - if (!lib_local) { - BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } + if (!lib_local) { + BKE_libblock_remap(bmain, ob, ob_new, ID_REMAP_SKIP_INDIRECT_USAGE); } } } -- cgit v1.2.3 From 0f6be4e1520087bfe6d1dc98b61d65686ae09b3f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 9 Sep 2021 10:51:39 +0200 Subject: Cleanup: Readfile: cleanup some logic checks. --- source/blender/blenloader/intern/readfile.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3e9ea8db758..9704e8bb413 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4500,7 +4500,8 @@ static void add_loose_objects_to_scene(Main *mainvar, * or for a collection when *lib has been set. */ LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; - if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { + if (do_it || + ((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { if (ob->id.us == 0) { do_it = true; @@ -4648,7 +4649,7 @@ static void add_collections_to_scene(Main *mainvar, LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 && - (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { + (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == false)) { do_add_collection = true; break; } -- cgit v1.2.3 From bf47fb40fd6f0ee9386e9936cf213a1049c55b61 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 9 Sep 2021 12:54:20 +0200 Subject: Geometry Nodes: fields and anonymous attributes This implements the initial core framework for fields and anonymous attributes (also see T91274). The new functionality is hidden behind the "Geometry Nodes Fields" feature flag. When enabled in the user preferences, the following new nodes become available: `Position`, `Index`, `Normal`, `Set Position` and `Attribute Capture`. Socket inspection has not been updated to work with fields yet. Besides these changes at the user level, this patch contains the ground work for: * building and evaluating fields at run-time (`FN_fields.hh`) and * creating and accessing anonymous attributes on geometry (`BKE_anonymous_attribute.h`). For evaluating fields we use a new so called multi-function procedure (`FN_multi_function_procedure.hh`). It allows composing multi-functions in arbitrary ways and supports efficient evaluation as is required by fields. See `FN_multi_function_procedure.hh` for more details on how this evaluation mechanism can be used. A new `AttributeIDRef` has been added which allows handling named and anonymous attributes in the same way in many places. Hans and I worked on this patch together. Differential Revision: https://developer.blender.org/D12414 --- release/scripts/startup/bl_ui/space_userpref.py | 1 + release/scripts/startup/nodeitems_builtins.py | 9 + .../blender/blenkernel/BKE_anonymous_attribute.h | 43 + .../blender/blenkernel/BKE_anonymous_attribute.hh | 169 +++ source/blender/blenkernel/BKE_attribute_access.hh | 98 +- source/blender/blenkernel/BKE_customdata.h | 12 + source/blender/blenkernel/BKE_geometry_set.hh | 114 +- .../blenkernel/BKE_geometry_set_instances.hh | 9 +- source/blender/blenkernel/BKE_node.h | 5 + source/blender/blenkernel/CMakeLists.txt | 3 + .../blenkernel/intern/anonymous_attribute.cc | 118 ++ .../blender/blenkernel/intern/attribute_access.cc | 422 ++++--- .../blenkernel/intern/attribute_access_intern.hh | 23 +- source/blender/blenkernel/intern/curve_eval.cc | 8 +- source/blender/blenkernel/intern/customdata.c | 53 +- .../blenkernel/intern/geometry_component_curve.cc | 24 +- .../blenkernel/intern/geometry_component_mesh.cc | 24 +- .../blenkernel/intern/geometry_set_instances.cc | 53 +- source/blender/blenkernel/intern/node.cc | 5 + .../spreadsheet_data_source_geometry.cc | 23 +- source/blender/functions/CMakeLists.txt | 11 + source/blender/functions/FN_field.hh | 456 ++++++++ source/blender/functions/FN_field_cpp_type.hh | 72 ++ .../blender/functions/FN_multi_function_builder.hh | 9 + .../blender/functions/FN_multi_function_params.hh | 2 +- .../functions/FN_multi_function_procedure.hh | 452 ++++++++ .../FN_multi_function_procedure_builder.hh | 260 +++++ .../FN_multi_function_procedure_executor.hh | 39 + source/blender/functions/intern/cpp_types.cc | 9 + source/blender/functions/intern/field.cc | 569 +++++++++ .../functions/intern/multi_function_builder.cc | 28 + .../functions/intern/multi_function_procedure.cc | 794 +++++++++++++ .../intern/multi_function_procedure_builder.cc | 175 +++ .../intern/multi_function_procedure_executor.cc | 1212 ++++++++++++++++++++ source/blender/functions/tests/FN_field_test.cc | 278 +++++ .../tests/FN_multi_function_procedure_test.cc | 344 ++++++ source/blender/makesdna/DNA_customdata_types.h | 9 + source/blender/makesdna/DNA_node_types.h | 7 + source/blender/makesdna/DNA_userdef_types.h | 3 +- source/blender/makesrna/intern/rna_nodetree.c | 20 + source/blender/makesrna/intern/rna_userdef.c | 4 + source/blender/modifiers/intern/MOD_nodes.cc | 21 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 63 +- source/blender/nodes/CMakeLists.txt | 5 + source/blender/nodes/NOD_geometry.h | 5 + source/blender/nodes/NOD_geometry_exec.hh | 72 +- source/blender/nodes/NOD_static_types.h | 7 +- .../blender/nodes/geometry/node_geometry_util.hh | 2 +- .../geometry/nodes/node_geo_attribute_capture.cc | 210 ++++ .../geometry/nodes/node_geo_curve_endpoints.cc | 51 +- .../geometry/nodes/node_geo_curve_resample.cc | 16 +- .../nodes/geometry/nodes/node_geo_curve_reverse.cc | 4 +- .../geometry/nodes/node_geo_curve_spline_type.cc | 8 +- .../geometry/nodes/node_geo_curve_subdivide.cc | 8 +- .../geometry/nodes/node_geo_curve_to_points.cc | 82 +- .../nodes/geometry/nodes/node_geo_curve_trim.cc | 16 +- .../geometry/nodes/node_geo_delete_geometry.cc | 8 +- .../nodes/geometry/nodes/node_geo_input_index.cc | 60 + .../nodes/geometry/nodes/node_geo_input_normal.cc | 211 ++++ .../geometry/nodes/node_geo_input_position.cc | 43 + .../nodes/geometry/nodes/node_geo_join_geometry.cc | 90 +- .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 21 +- .../geometry/nodes/node_geo_point_distribute.cc | 16 +- .../geometry/nodes/node_geo_point_separate.cc | 6 +- .../nodes/geometry/nodes/node_geo_set_position.cc | 79 ++ .../nodes/intern/geometry_nodes_eval_log.cc | 6 +- source/blender/nodes/intern/node_socket.cc | 63 +- 67 files changed, 6674 insertions(+), 468 deletions(-) create mode 100644 source/blender/blenkernel/BKE_anonymous_attribute.h create mode 100644 source/blender/blenkernel/BKE_anonymous_attribute.hh create mode 100644 source/blender/blenkernel/intern/anonymous_attribute.cc create mode 100644 source/blender/functions/FN_field.hh create mode 100644 source/blender/functions/FN_field_cpp_type.hh create mode 100644 source/blender/functions/FN_multi_function_procedure.hh create mode 100644 source/blender/functions/FN_multi_function_procedure_builder.hh create mode 100644 source/blender/functions/FN_multi_function_procedure_executor.hh create mode 100644 source/blender/functions/intern/field.cc create mode 100644 source/blender/functions/intern/multi_function_procedure.cc create mode 100644 source/blender/functions/intern/multi_function_procedure_builder.cc create mode 100644 source/blender/functions/intern/multi_function_procedure_executor.cc create mode 100644 source/blender/functions/tests/FN_field_test.cc create mode 100644 source/blender/functions/tests/FN_multi_function_procedure_test.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_index.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_normal.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_input_position.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_set_position.cc diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 1ac19d020d8..0d1cbc424d7 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2254,6 +2254,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), + ({"property": "use_geometry_nodes_fields"}, "T91274"), ), ) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 668ea4bfae5..114e654e22b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -180,6 +180,10 @@ def object_eevee_cycles_shader_nodes_poll(context): eevee_cycles_shader_nodes_poll(context)) +def geometry_nodes_fields_poll(context): + return context.preferences.experimental.use_geometry_nodes_fields + + # All standard node categories currently used in nodes. shader_node_categories = [ @@ -478,6 +482,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), + NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeAttributeClamp"), NodeItem("GeometryNodeAttributeCompare"), NodeItem("GeometryNodeAttributeConvert"), @@ -534,6 +539,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), NodeItem("GeometryNodeRaycast"), + NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("GeometryNodeObjectInfo"), @@ -544,6 +550,9 @@ geometry_node_categories = [ NodeItem("FunctionNodeInputVector"), NodeItem("GeometryNodeInputMaterial"), NodeItem("GeometryNodeIsViewport"), + NodeItem("GeometryNodeInputPosition", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeInputIndex", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeInputNormal", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ NodeItem("GeometryNodeMaterialAssign"), diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.h b/source/blender/blenkernel/BKE_anonymous_attribute.h new file mode 100644 index 00000000000..ebdb0b05160 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup bke + * + * An #AnonymousAttributeID is used to identify attributes that are not explicitly named. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AnonymousAttributeID AnonymousAttributeID; + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name); +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name); +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id); +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id); +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.hh b/source/blender/blenkernel/BKE_anonymous_attribute.hh new file mode 100644 index 00000000000..201fa2b2f52 --- /dev/null +++ b/source/blender/blenkernel/BKE_anonymous_attribute.hh @@ -0,0 +1,169 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include "BLI_hash.hh" +#include "BLI_string_ref.hh" + +#include "BKE_anonymous_attribute.h" + +namespace blender::bke { + +/** + * Wrapper for #AnonymousAttributeID with RAII semantics. + * This class should typically not be used directly. Instead use #StrongAnonymousAttributeID or + * #WeakAnonymousAttributeID. + */ +template class OwnedAnonymousAttributeID { + private: + const AnonymousAttributeID *data_ = nullptr; + + template friend class OwnedAnonymousAttributeID; + + public: + OwnedAnonymousAttributeID() = default; + + /** Create a new anonymous attribute id. */ + explicit OwnedAnonymousAttributeID(StringRefNull debug_name) + { + if constexpr (IsStrongReference) { + data_ = BKE_anonymous_attribute_id_new_strong(debug_name.c_str()); + } + else { + data_ = BKE_anonymous_attribute_id_new_weak(debug_name.c_str()); + } + } + + /** + * This transfers ownership, so no incref is necessary. + * The caller has to make sure that it owned the anonymous id. + */ + explicit OwnedAnonymousAttributeID(const AnonymousAttributeID *anonymous_id) + : data_(anonymous_id) + { + } + + template + OwnedAnonymousAttributeID(const OwnedAnonymousAttributeID &other) + { + data_ = other.data_; + this->incref(); + } + + template + OwnedAnonymousAttributeID(OwnedAnonymousAttributeID &&other) + { + data_ = other.data_; + this->incref(); + other.decref(); + other.data_ = nullptr; + } + + ~OwnedAnonymousAttributeID() + { + this->decref(); + } + + template + OwnedAnonymousAttributeID &operator=(const OwnedAnonymousAttributeID &other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(other); + return *this; + } + + template + OwnedAnonymousAttributeID &operator=(OwnedAnonymousAttributeID &&other) + { + if (this == &other) { + return *this; + } + this->~OwnedAnonymousAttributeID(); + new (this) OwnedAnonymousAttributeID(std::move(other)); + return *this; + } + + operator bool() const + { + return data_ != nullptr; + } + + StringRefNull debug_name() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_debug_name(data_); + } + + bool has_strong_references() const + { + BLI_assert(data_ != nullptr); + return BKE_anonymous_attribute_id_has_strong_references(data_); + } + + /** Extract the onwership of the currently wrapped anonymous id. */ + const AnonymousAttributeID *extract() + { + const AnonymousAttributeID *extracted_data = data_; + /* Don't decref because the caller becomes the new owner. */ + data_ = nullptr; + return extracted_data; + } + + /** Get the wrapped anonymous id, without taking ownership. */ + const AnonymousAttributeID *get() const + { + return data_; + } + + private: + void incref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_increment_strong(data_); + } + else { + BKE_anonymous_attribute_id_increment_weak(data_); + } + } + + void decref() + { + if (data_ == nullptr) { + return; + } + if constexpr (IsStrongReference) { + BKE_anonymous_attribute_id_decrement_strong(data_); + } + else { + BKE_anonymous_attribute_id_decrement_weak(data_); + } + } +}; + +using StrongAnonymousAttributeID = OwnedAnonymousAttributeID; +using WeakAnonymousAttributeID = OwnedAnonymousAttributeID; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c3f7dbd4bd9..9d309d8a1c1 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -22,6 +22,7 @@ #include "FN_generic_span.hh" #include "FN_generic_virtual_array.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute.h" #include "BLI_color.hh" @@ -29,6 +30,81 @@ #include "BLI_float3.hh" #include "BLI_function_ref.hh" +namespace blender::bke { + +/** + * Identifies an attribute that is either named or anonymous. + * It does not own the identifier, so it is just a reference. + */ +class AttributeIDRef { + private: + StringRef name_; + const AnonymousAttributeID *anonymous_id_ = nullptr; + + public: + AttributeIDRef() = default; + + AttributeIDRef(StringRef name) : name_(name) + { + } + + AttributeIDRef(StringRefNull name) : name_(name) + { + } + + AttributeIDRef(const char *name) : name_(name) + { + } + + AttributeIDRef(const std::string &name) : name_(name) + { + } + + /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ + AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id) + { + } + + operator bool() const + { + return this->is_named() || this->is_anonymous(); + } + + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) + { + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; + } + + uint64_t hash() const + { + return get_default_hash_2(name_, anonymous_id_); + } + + bool is_named() const + { + return !name_.is_empty(); + } + + bool is_anonymous() const + { + return anonymous_id_ != nullptr; + } + + StringRef name() const + { + BLI_assert(this->is_named()); + return name_; + } + + const AnonymousAttributeID &anonymous_id() const + { + BLI_assert(this->is_anonymous()); + return *anonymous_id_; + } +}; + +} // namespace blender::bke + /** * Contains information about an attribute in a geometry component. * More information can be added in the future. E.g. whether the attribute is builtin and how it is @@ -104,8 +180,8 @@ struct AttributeInitMove : public AttributeInit { }; /* Returns false when the iteration should be stopped. */ -using AttributeForeachCallback = blender::FunctionRef; +using AttributeForeachCallback = blender::FunctionRef; namespace blender::bke { @@ -333,26 +409,28 @@ class CustomDataAttributes { void reallocate(const int size); - std::optional get_for_read(const blender::StringRef name) const; + std::optional get_for_read(const AttributeIDRef &attribute_id) const; - blender::fn::GVArrayPtr get_for_read(const StringRef name, + blender::fn::GVArrayPtr get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const; template - blender::fn::GVArray_Typed get_for_read(const blender::StringRef name, + blender::fn::GVArray_Typed get_for_read(const AttributeIDRef &attribute_id, const T &default_value) const { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - GVArrayPtr varray = this->get_for_read(name, type, &default_value); + GVArrayPtr varray = this->get_for_read(attribute_id, type, &default_value); return blender::fn::GVArray_Typed(std::move(varray)); } - std::optional get_for_write(const blender::StringRef name); - bool create(const blender::StringRef name, const CustomDataType data_type); - bool create_by_move(const blender::StringRef name, const CustomDataType data_type, void *buffer); - bool remove(const blender::StringRef name); + std::optional get_for_write(const AttributeIDRef &attribute_id); + bool create(const AttributeIDRef &attribute_id, const CustomDataType data_type); + bool create_by_move(const AttributeIDRef &attribute_id, + const CustomDataType data_type, + void *buffer); + bool remove(const AttributeIDRef &attribute_id); bool foreach_attribute(const AttributeForeachCallback callback, const AttributeDomain domain) const; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 7a44553c565..0732f1e5190 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -33,6 +33,7 @@ extern "C" { #endif +struct AnonymousAttributeID; struct BMesh; struct BlendDataReader; struct BlendWriter; @@ -193,6 +194,12 @@ void *CustomData_add_layer_named(struct CustomData *data, void *layer, int totelem, const char *name); +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layer, + int totelem, + const struct AnonymousAttributeID *anonymous_id); /* frees the active or first data layer with the give type. * returns 1 on success, 0 if no layer with the given type is found @@ -231,6 +238,11 @@ void *CustomData_duplicate_referenced_layer_named(struct CustomData *data, const int type, const char *name, const int totelem); +void *CustomData_duplicate_referenced_layer_anonymous( + CustomData *data, + const int type, + const struct AnonymousAttributeID *anonymous_id, + const int totelem); bool CustomData_is_referenced_layer(struct CustomData *data, int type); /* Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. */ diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 08b6cb951a8..c3d594d7dcd 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -31,9 +31,12 @@ #include "BLI_user_counter.hh" #include "BLI_vector_set.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.h" +#include "FN_field.hh" + struct Collection; struct Curve; struct CurveEval; @@ -88,11 +91,11 @@ class GeometryComponent { GeometryComponentType type() const; /* Return true when any attribute with this name exists, including built in attributes. */ - bool attribute_exists(const blender::StringRef attribute_name) const; + bool attribute_exists(const blender::bke::AttributeIDRef &attribute_id) const; /* Return the data type and domain of an attribute with the given name if it exists. */ std::optional attribute_get_meta_data( - const blender::StringRef attribute_name) const; + const blender::bke::AttributeIDRef &attribute_id) const; /* Returns true when the geometry component supports this attribute domain. */ bool attribute_domain_supported(const AttributeDomain domain) const; @@ -104,12 +107,12 @@ class GeometryComponent { /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::StringRef attribute_name) const; + const blender::bke::AttributeIDRef &attribute_id) const; /* Get read and write access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ blender::bke::WriteAttributeLookup attribute_try_get_for_write( - const blender::StringRef attribute_name); + const blender::bke::AttributeIDRef &attribute_id); /* Get a read-only attribute for the domain based on the given attribute. This can be used to * interpolate from one domain to another. @@ -120,10 +123,10 @@ class GeometryComponent { const AttributeDomain to_domain) const; /* Returns true when the attribute has been deleted. */ - bool attribute_try_delete(const blender::StringRef attribute_name); + bool attribute_try_delete(const blender::bke::AttributeIDRef &attribute_id); /* Returns true when the attribute has been created. */ - bool attribute_try_create(const blender::StringRef attribute_name, + bool attribute_try_create(const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer); @@ -133,7 +136,7 @@ class GeometryComponent { bool attribute_try_create_builtin(const blender::StringRef attribute_name, const AttributeInit &initializer); - blender::Set attribute_names() const; + blender::Set attribute_ids() const; bool attribute_foreach(const AttributeForeachCallback callback) const; virtual bool is_empty() const; @@ -142,7 +145,7 @@ class GeometryComponent { * Returns null when the attribute does not exist or cannot be converted to the requested domain * and data type. */ std::unique_ptr attribute_try_get_for_read( - const blender::StringRef attribute_name, + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const; @@ -150,18 +153,18 @@ class GeometryComponent { * left unchanged. Returns null when the attribute does not exist or cannot be adapted to the * requested domain. */ std::unique_ptr attribute_try_get_for_read( - const blender::StringRef attribute_name, const AttributeDomain domain) const; + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) const; /* Get a virtual array to read data of an attribute with the given data type. The domain is * left unchanged. Returns null when the attribute does not exist or cannot be converted to the * requested data type. */ blender::bke::ReadAttributeLookup attribute_try_get_for_read( - const blender::StringRef attribute_name, const CustomDataType data_type) const; + const blender::bke::AttributeIDRef &attribute_id, const CustomDataType data_type) const; /* Get a virtual array to read the data of an attribute. If that is not possible, the returned * virtual array will contain a default value. This never returns null. */ std::unique_ptr attribute_get_for_read( - const blender::StringRef attribute_name, + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr) const; @@ -169,14 +172,15 @@ class GeometryComponent { /* Should be used instead of the method above when the requested data type is known at compile * time for better type safety. */ template - blender::fn::GVArray_Typed attribute_get_for_read(const blender::StringRef attribute_name, - const AttributeDomain domain, - const T &default_value) const + blender::fn::GVArray_Typed attribute_get_for_read( + const blender::bke::AttributeIDRef &attribute_id, + const AttributeDomain domain, + const T &default_value) const { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get(); const CustomDataType type = blender::bke::cpp_type_to_custom_data_type(cpp_type); std::unique_ptr varray = this->attribute_get_for_read( - attribute_name, domain, type, &default_value); + attribute_id, domain, type, &default_value); return blender::fn::GVArray_Typed(std::move(varray)); } @@ -191,7 +195,7 @@ class GeometryComponent { * is created that will overwrite the existing attribute in the end. */ blender::bke::OutputAttribute attribute_try_get_for_output( - const blender::StringRef attribute_name, + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value = nullptr); @@ -200,28 +204,30 @@ class GeometryComponent { * attributes are not read, i.e. the attribute is used only for output. Since values are not read * from this attribute, no default value is necessary. */ blender::bke::OutputAttribute attribute_try_get_for_output_only( - const blender::StringRef attribute_name, + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type); /* Statically typed method corresponding to the equally named generic one. */ template blender::bke::OutputAttribute_Typed attribute_try_get_for_output( - const blender::StringRef attribute_name, const AttributeDomain domain, const T default_value) + const blender::bke::AttributeIDRef &attribute_id, + const AttributeDomain domain, + const T default_value) { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get(); const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_try_get_for_output(attribute_name, domain, data_type, &default_value); + return this->attribute_try_get_for_output(attribute_id, domain, data_type, &default_value); } /* Statically typed method corresponding to the equally named generic one. */ template blender::bke::OutputAttribute_Typed attribute_try_get_for_output_only( - const blender::StringRef attribute_name, const AttributeDomain domain) + const blender::bke::AttributeIDRef &attribute_id, const AttributeDomain domain) { const blender::fn::CPPType &cpp_type = blender::fn::CPPType::get(); const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(cpp_type); - return this->attribute_try_get_for_output_only(attribute_name, domain, data_type); + return this->attribute_try_get_for_output_only(attribute_id, domain, data_type); } private: @@ -616,3 +622,69 @@ class VolumeComponent : public GeometryComponent { static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME; }; + +namespace blender::bke { + +class GeometryComponentFieldContext : public fn::FieldContext { + private: + const GeometryComponent &component_; + const AttributeDomain domain_; + + public: + GeometryComponentFieldContext(const GeometryComponent &component, const AttributeDomain domain) + : component_(component), domain_(domain) + { + } + + const GeometryComponent &geometry_component() const + { + return component_; + } + + AttributeDomain domain() const + { + return domain_; + } +}; + +class AttributeFieldInput : public fn::FieldInput { + private: + std::string name_; + + public: + AttributeFieldInput(std::string name, const CPPType &type) + : fn::FieldInput(type, name), name_(std::move(name)) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +class AnonymousAttributeFieldInput : public fn::FieldInput { + private: + /** + * A strong reference is required to make sure that the referenced attribute is not removed + * automatically. + */ + StrongAnonymousAttributeID anonymous_id_; + + public: + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) + : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 25876296a47..44a0ee30c4c 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -59,9 +59,10 @@ struct AttributeKind { * will contain the highest complexity data type and the highest priority domain among every * attribute with the given name on all of the input components. */ -void geometry_set_gather_instances_attribute_info(Span set_groups, - Span component_types, - const Set &ignored_attributes, - Map &r_attributes); +void geometry_set_gather_instances_attribute_info( + Span set_groups, + Span component_types, + const Set &ignored_attributes, + Map &r_attributes); } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index b41fbae6075..0d70e812ecb 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1484,6 +1484,11 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SPLINE_TYPE 1073 #define GEO_NODE_CURVE_SELECT_HANDLES 1074 #define GEO_NODE_CURVE_FILL 1075 +#define GEO_NODE_INPUT_POSITION 1076 +#define GEO_NODE_SET_POSITION 1077 +#define GEO_NODE_INPUT_INDEX 1078 +#define GEO_NODE_INPUT_NORMAL 1079 +#define GEO_NODE_ATTRIBUTE_CAPTURE 1080 /** \} */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 64cfbd8f491..26d81ec3b34 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -76,6 +76,7 @@ set(SRC intern/anim_path.c intern/anim_sys.c intern/anim_visualization.c + intern/anonymous_attribute.cc intern/appdir.c intern/armature.c intern/armature_deform.c @@ -295,6 +296,8 @@ set(SRC BKE_anim_path.h BKE_anim_visualization.h BKE_animsys.h + BKE_anonymous_attribute.h + BKE_anonymous_attribute.hh BKE_appdir.h BKE_armature.h BKE_armature.hh diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc new file mode 100644 index 00000000000..67611053d83 --- /dev/null +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include "BKE_anonymous_attribute.hh" + +using namespace blender::bke; + +/** + * A struct that identifies an attribute. It's lifetime is managed by an atomic reference count. + * + * Additionally, this struct can be strongly or weakly owned. The difference is that strong + * ownership means that attributes with this id will be kept around. Weak ownership just makes sure + * that the struct itself stays alive, but corresponding attributes might still be removed + * automatically. + */ +struct AnonymousAttributeID { + /** + * Total number of references to this attribute id. Once this reaches zero, the struct can be + * freed. This includes strong and weak references. + */ + mutable std::atomic refcount_tot = 0; + + /** + * Number of strong references to this attribute id. When this is zero, the corresponding + * attributes can be removed from geometries automatically. + */ + mutable std::atomic refcount_strong = 0; + + /** + * Only used to identify this struct in a debugging session. + */ + std::string debug_name; + + /** + * Unique name of the this attribute id during the current session. + */ + std::string internal_name; +}; + +/** Every time this function is called, it outputs a different name. */ +static std::string get_new_internal_name() +{ + static std::atomic index = 0; + const int next_index = index.fetch_add(1); + return "anonymous_attribute_" + std::to_string(next_index); +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_weak(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + return anonymous_id; +} + +AnonymousAttributeID *BKE_anonymous_attribute_id_new_strong(const char *debug_name) +{ + AnonymousAttributeID *anonymous_id = new AnonymousAttributeID(); + anonymous_id->debug_name = debug_name; + anonymous_id->internal_name = get_new_internal_name(); + anonymous_id->refcount_tot.store(1); + anonymous_id->refcount_strong.store(1); + return anonymous_id; +} + +bool BKE_anonymous_attribute_id_has_strong_references(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->refcount_strong.load() >= 1; +} + +void BKE_anonymous_attribute_id_increment_weak(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); +} + +void BKE_anonymous_attribute_id_increment_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_tot.fetch_add(1); + anonymous_id->refcount_strong.fetch_add(1); +} + +void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anonymous_id) +{ + const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; + if (new_refcount == 0) { + delete anonymous_id; + } +} + +void BKE_anonymous_attribute_id_decrement_strong(const AnonymousAttributeID *anonymous_id) +{ + anonymous_id->refcount_strong.fetch_sub(1); + BKE_anonymous_attribute_id_decrement_weak(anonymous_id); +} + +const char *BKE_anonymous_attribute_id_debug_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->debug_name.c_str(); +} + +const char *BKE_anonymous_attribute_id_internal_name(const AnonymousAttributeID *anonymous_id) +{ + return anonymous_id->internal_name.c_str(); +} diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index aa0af294bc3..08bd5dc5981 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -44,6 +44,8 @@ using blender::float3; using blender::Set; using blender::StringRef; using blender::StringRefNull; +using blender::bke::AttributeIDRef; +using blender::bke::OutputAttribute; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; @@ -334,8 +336,20 @@ bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) return data != nullptr; } +static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, + const AttributeIDRef &attribute_id) +{ + if (!attribute_id) { + return false; + } + if (attribute_id.is_anonymous()) { + return layer.anonymous_id == &attribute_id.anonymous_id(); + } + return layer.name == attribute_id.name(); +} + ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -343,7 +357,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } const int domain_size = component.attribute_domain_size(domain_); for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } const CustomDataType data_type = (CustomDataType)layer.type; @@ -368,7 +382,7 @@ ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( } WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -376,10 +390,17 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } const int domain_size = component.attribute_domain_size(domain_); for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { - if (layer.name != attribute_name) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { continue; } - CustomData_duplicate_referenced_layer_named(custom_data, layer.type, layer.name, domain_size); + if (attribute_id.is_named()) { + CustomData_duplicate_referenced_layer_named( + custom_data, layer.type, layer.name, domain_size); + } + else { + CustomData_duplicate_referenced_layer_anonymous( + custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + } const CustomDataType data_type = (CustomDataType)layer.type; switch (data_type) { case CD_PROP_FLOAT: @@ -402,7 +423,7 @@ WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( } bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -411,7 +432,8 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, const int domain_size = component.attribute_domain_size(domain_); for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; - if (this->type_is_supported((CustomDataType)layer.type) && layer.name == attribute_name) { + if (this->type_is_supported((CustomDataType)layer.type) && + custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(custom_data, layer.type, domain_size, i); return true; } @@ -419,24 +441,39 @@ bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, return false; } -static bool add_named_custom_data_layer_from_attribute_init(const StringRef attribute_name, - CustomData &custom_data, - const CustomDataType data_type, - const int domain_size, - const AttributeInit &initializer) +static void *add_generic_custom_data_layer(CustomData &custom_data, + const CustomDataType data_type, + const eCDAllocType alloctype, + void *layer_data, + const int domain_size, + const AttributeIDRef &attribute_id) { - char attribute_name_c[MAX_NAME]; - attribute_name.copy(attribute_name_c); + if (attribute_id.is_named()) { + char attribute_name_c[MAX_NAME]; + attribute_id.name().copy(attribute_name_c); + return CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + } + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + return CustomData_add_layer_anonymous( + &custom_data, data_type, alloctype, layer_data, domain_size, &anonymous_id); +} +static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attribute_id, + CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) +{ switch (initializer.type) { case AttributeInit::Type::Default: { - void *data = CustomData_add_layer_named( - &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); return data != nullptr; } case AttributeInit::Type::VArray: { - void *data = CustomData_add_layer_named( - &custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_name_c); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_DEFAULT, nullptr, domain_size, attribute_id); if (data == nullptr) { return false; } @@ -446,8 +483,8 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } case AttributeInit::Type::MoveArray: { void *source_data = static_cast(initializer).data; - void *data = CustomData_add_layer_named( - &custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_name_c); + void *data = add_generic_custom_data_layer( + custom_data, data_type, CD_ASSIGN, source_data, domain_size, attribute_id); if (data == nullptr) { MEM_freeN(source_data); return false; @@ -461,7 +498,7 @@ static bool add_named_custom_data_layer_from_attribute_init(const StringRef attr } bool CustomDataAttributeProvider::try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const @@ -477,13 +514,13 @@ bool CustomDataAttributeProvider::try_create(GeometryComponent &component, return false; } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { return false; } } const int domain_size = component.attribute_domain_size(domain_); - add_named_custom_data_layer_from_attribute_init( - attribute_name, *custom_data, data_type, domain_size, initializer); + add_custom_data_layer_from_attribute_init( + attribute_id, *custom_data, data_type, domain_size, initializer); return true; } @@ -498,7 +535,14 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com const CustomDataType data_type = (CustomDataType)layer.type; if (this->type_is_supported(data_type)) { AttributeMetaData meta_data{domain_, data_type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -507,7 +551,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com } ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( - const GeometryComponent &component, const StringRef attribute_name) const + const GeometryComponent &component, const AttributeIDRef &attribute_id) const { const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); if (custom_data == nullptr) { @@ -515,7 +559,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); return {as_read_attribute_(layer.data, domain_size), domain_}; } @@ -525,7 +569,7 @@ ReadAttributeLookup NamedLegacyCustomDataProvider::try_get_for_read( } WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( - GeometryComponent &component, const StringRef attribute_name) const + GeometryComponent &component, const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -533,7 +577,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); void *data_old = layer.data; void *data_new = CustomData_duplicate_referenced_layer_named( @@ -549,7 +593,7 @@ WriteAttributeLookup NamedLegacyCustomDataProvider::try_get_for_write( } bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { CustomData *custom_data = custom_data_access_.get_custom_data(component); if (custom_data == nullptr) { @@ -558,7 +602,7 @@ bool NamedLegacyCustomDataProvider::try_delete(GeometryComponent &component, for (const int i : IndexRange(custom_data->totlayer)) { const CustomDataLayer &layer = custom_data->layers[i]; if (layer.type == stored_type_) { - if (layer.name == attribute_name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const int domain_size = component.attribute_domain_size(domain_); CustomData_free_layer(custom_data, stored_type_, domain_size, i); custom_data_access_.update_custom_data_pointers(component); @@ -627,11 +671,11 @@ CustomDataAttributes &CustomDataAttributes::operator=(const CustomDataAttributes return *this; } -std::optional CustomDataAttributes::get_for_read(const StringRef name) const +std::optional CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id) const { BLI_assert(size_ != 0); for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GSpan(*cpp_type, layer.data, size_); @@ -645,13 +689,13 @@ std::optional CustomDataAttributes::get_for_read(const StringRef name) co * value if the attribute doesn't exist. If no default value is provided, the default value for the * type will be used. */ -GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, +GVArrayPtr CustomDataAttributes::get_for_read(const AttributeIDRef &attribute_id, const CustomDataType data_type, const void *default_value) const { const CPPType *type = blender::bke::custom_data_type_to_cpp_type(data_type); - std::optional attribute = this->get_for_read(name); + std::optional attribute = this->get_for_read(attribute_id); if (!attribute) { const int domain_size = this->size_; return std::make_unique( @@ -666,12 +710,12 @@ GVArrayPtr CustomDataAttributes::get_for_read(const StringRef name, return conversions.try_convert(std::make_unique(*attribute), *type); } -std::optional CustomDataAttributes::get_for_write(const StringRef name) +std::optional CustomDataAttributes::get_for_write(const AttributeIDRef &attribute_id) { /* If this assert hits, it most likely means that #reallocate was not called at some point. */ BLI_assert(size_ != 0); for (CustomDataLayer &layer : MutableSpan(data.layers, data.totlayer)) { - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { const CPPType *cpp_type = custom_data_type_to_cpp_type((CustomDataType)layer.type); BLI_assert(cpp_type != nullptr); return GMutableSpan(*cpp_type, layer.data, size_); @@ -680,30 +724,29 @@ std::optional CustomDataAttributes::get_for_write(const StringRef return {}; } -bool CustomDataAttributes::create(const StringRef name, const CustomDataType data_type) +bool CustomDataAttributes::create(const AttributeIDRef &attribute_id, + const CustomDataType data_type) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_DEFAULT, nullptr, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_DEFAULT, nullptr, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::create_by_move(const blender::StringRef name, +bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id, const CustomDataType data_type, void *buffer) { - char name_c[MAX_NAME]; - name.copy(name_c); - void *result = CustomData_add_layer_named(&data, data_type, CD_ASSIGN, buffer, size_, name_c); + void *result = add_generic_custom_data_layer( + data, data_type, CD_ASSIGN, buffer, size_, attribute_id); return result != nullptr; } -bool CustomDataAttributes::remove(const blender::StringRef name) +bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id) { bool result = false; for (const int i : IndexRange(data.totlayer)) { const CustomDataLayer &layer = data.layers[i]; - if (layer.name == name) { + if (custom_data_layer_matches_attribute_id(layer, attribute_id)) { CustomData_free_layer(&data, layer.type, size_, i); result = true; } @@ -722,7 +765,14 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { AttributeMetaData meta_data{domain, (CustomDataType)layer.type}; - if (!callback(layer.name, meta_data)) { + AttributeIDRef attribute_id; + if (layer.anonymous_id != nullptr) { + attribute_id = layer.anonymous_id; + } + else { + attribute_id = layer.name; + } + if (!callback(attribute_id, meta_data)) { return false; } } @@ -766,21 +816,23 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_ } blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_read(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_name); + ReadAttributeLookup attribute = dynamic_provider->try_get_for_read(*this, attribute_id); if (attribute) { return attribute; } @@ -800,21 +852,23 @@ std::unique_ptr GeometryComponent::attribute_try_adapt_dom } blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_write( - const StringRef attribute_name) + const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + } } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_name); + WriteAttributeLookup attribute = dynamic_provider->try_get_for_write(*this, attribute_id); if (attribute) { return attribute; } @@ -822,53 +876,57 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ return {}; } -bool GeometryComponent::attribute_try_delete(const StringRef attribute_name) +bool GeometryComponent::attribute_try_delete(const AttributeIDRef &attribute_id) { using namespace blender::bke; const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return {}; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - return builtin_provider->try_delete(*this); + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + return builtin_provider->try_delete(*this); + } } bool success = false; for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - success = dynamic_provider->try_delete(*this, attribute_name) || success; + success = dynamic_provider->try_delete(*this, attribute_id) || success; } return success; } -bool GeometryComponent::attribute_try_create(const StringRef attribute_name, +bool GeometryComponent::attribute_try_create(const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) { using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { return false; } const ComponentAttributeProviders *providers = this->get_attribute_providers(); if (providers == nullptr) { return false; } - const BuiltinAttributeProvider *builtin_provider = - providers->builtin_attribute_providers().lookup_default_as(attribute_name, nullptr); - if (builtin_provider != nullptr) { - if (builtin_provider->domain() != domain) { - return false; - } - if (builtin_provider->data_type() != data_type) { - return false; + if (attribute_id.is_named()) { + const BuiltinAttributeProvider *builtin_provider = + providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); + if (builtin_provider != nullptr) { + if (builtin_provider->domain() != domain) { + return false; + } + if (builtin_provider->data_type() != data_type) { + return false; + } + return builtin_provider->try_create(*this, initializer); } - return builtin_provider->try_create(*this, initializer); } for (const DynamicAttributesProvider *dynamic_provider : providers->dynamic_attribute_providers()) { - if (dynamic_provider->try_create(*this, attribute_name, domain, data_type, initializer)) { + if (dynamic_provider->try_create(*this, attribute_id, domain, data_type, initializer)) { return true; } } @@ -894,13 +952,14 @@ bool GeometryComponent::attribute_try_create_builtin(const blender::StringRef at return builtin_provider->try_create(*this, initializer); } -Set GeometryComponent::attribute_names() const +Set GeometryComponent::attribute_ids() const { - Set attributes; - this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - attributes.add(name); - return true; - }); + Set attributes; + this->attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + attributes.add(attribute_id); + return true; + }); return attributes; } @@ -931,9 +990,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac } for (const DynamicAttributesProvider *provider : providers->dynamic_attribute_providers()) { const bool continue_loop = provider->foreach_attribute( - *this, [&](StringRefNull name, const AttributeMetaData &meta_data) { - if (handled_attribute_names.add(name)) { - return callback(name, meta_data); + *this, [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_anonymous() || handled_attribute_names.add(attribute_id.name())) { + return callback(attribute_id, meta_data); } return true; }); @@ -945,9 +1004,9 @@ bool GeometryComponent::attribute_foreach(const AttributeForeachCallback callbac return true; } -bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const +bool GeometryComponent::attribute_exists(const AttributeIDRef &attribute_id) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (attribute) { return true; } @@ -955,16 +1014,17 @@ bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name } std::optional GeometryComponent::attribute_get_meta_data( - const StringRef attribute_name) const + const AttributeIDRef &attribute_id) const { std::optional result{std::nullopt}; - this->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (attribute_name == name) { - result = meta_data; - return false; - } - return true; - }); + this->attribute_foreach( + [&](const AttributeIDRef ¤t_attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id == current_attribute_id) { + result = meta_data; + return false; + } + return true; + }); return result; } @@ -977,11 +1037,11 @@ static std::unique_ptr try_adapt_data_type( } std::unique_ptr GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1007,13 +1067,13 @@ std::unique_ptr GeometryComponent::attribute_try_get_for_r } std::unique_ptr GeometryComponent::attribute_try_get_for_read( - const StringRef attribute_name, const AttributeDomain domain) const + const AttributeIDRef &attribute_id, const AttributeDomain domain) const { if (!this->attribute_domain_supported(domain)) { return {}; } - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1026,9 +1086,9 @@ std::unique_ptr GeometryComponent::attribute_try_get_for_ } blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( - const blender::StringRef attribute_name, const CustomDataType data_type) const + const AttributeIDRef &attribute_id, const CustomDataType data_type) const { - blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_name); + blender::bke::ReadAttributeLookup attribute = this->attribute_try_get_for_read(attribute_id); if (!attribute) { return {}; } @@ -1043,13 +1103,13 @@ blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( } std::unique_ptr GeometryComponent::attribute_get_for_read( - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const void *default_value) const { std::unique_ptr varray = this->attribute_try_get_for_read( - attribute_name, domain, data_type); + attribute_id, domain, data_type); if (varray) { return varray; } @@ -1065,15 +1125,22 @@ class GVMutableAttribute_For_OutputAttribute : public blender::fn::GVMutableArray_For_GMutableSpan { public: GeometryComponent *component; - std::string final_name; + std::string attribute_name; + blender::bke::WeakAnonymousAttributeID anonymous_attribute_id; GVMutableAttribute_For_OutputAttribute(GMutableSpan data, GeometryComponent &component, - std::string final_name) - : blender::fn::GVMutableArray_For_GMutableSpan(data), - component(&component), - final_name(std::move(final_name)) + const AttributeIDRef &attribute_id) + : blender::fn::GVMutableArray_For_GMutableSpan(data), component(&component) { + if (attribute_id.is_named()) { + this->attribute_name = attribute_id.name(); + } + else { + const AnonymousAttributeID *anonymous_id = &attribute_id.anonymous_id(); + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + this->anonymous_attribute_id = blender::bke::WeakAnonymousAttributeID{anonymous_id}; + } } ~GVMutableAttribute_For_OutputAttribute() override @@ -1083,7 +1150,7 @@ class GVMutableAttribute_For_OutputAttribute } }; -static void save_output_attribute(blender::bke::OutputAttribute &output_attribute) +static void save_output_attribute(OutputAttribute &output_attribute) { using namespace blender; using namespace blender::fn; @@ -1093,21 +1160,28 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut dynamic_cast(output_attribute.varray()); GeometryComponent &component = *varray.component; - const StringRefNull name = varray.final_name; + AttributeIDRef attribute_id; + if (!varray.attribute_name.empty()) { + attribute_id = varray.attribute_name; + } + else { + attribute_id = varray.anonymous_attribute_id.extract(); + } const AttributeDomain domain = output_attribute.domain(); const CustomDataType data_type = output_attribute.custom_data_type(); const CPPType &cpp_type = output_attribute.cpp_type(); - component.attribute_try_delete(name); - if (!component.attribute_try_create( - varray.final_name, domain, data_type, AttributeInitDefault())) { - CLOG_WARN(&LOG, - "Could not create the '%s' attribute with type '%s'.", - name.c_str(), - cpp_type.name().c_str()); + component.attribute_try_delete(attribute_id); + if (!component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault())) { + if (!varray.attribute_name.empty()) { + CLOG_WARN(&LOG, + "Could not create the '%s' attribute with type '%s'.", + varray.attribute_name.c_str(), + cpp_type.name().c_str()); + } return; } - WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), buffer); for (const int i : IndexRange(varray.size())) { varray.get(i, buffer); @@ -1115,19 +1189,18 @@ static void save_output_attribute(blender::bke::OutputAttribute &output_attribut } } -static blender::bke::OutputAttribute create_output_attribute( - GeometryComponent &component, - const blender::StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const bool ignore_old_values, - const void *default_value) +static OutputAttribute create_output_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const bool ignore_old_values, + const void *default_value) { using namespace blender; using namespace blender::fn; using namespace blender::bke; - if (attribute_name.is_empty()) { + if (!attribute_id) { return {}; } @@ -1135,7 +1208,8 @@ static blender::bke::OutputAttribute create_output_attribute( BLI_assert(cpp_type != nullptr); const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (component.attribute_is_builtin(attribute_name)) { + if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) { + const StringRef attribute_name = attribute_id.name(); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { if (default_value) { @@ -1169,18 +1243,18 @@ static blender::bke::OutputAttribute create_output_attribute( const int domain_size = component.attribute_domain_size(domain); - WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); + WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { if (default_value) { const GVArray_For_SingleValueRef default_varray{*cpp_type, domain_size, default_value}; component.attribute_try_create( - attribute_name, domain, data_type, AttributeInitVArray(&default_varray)); + attribute_id, domain, data_type, AttributeInitVArray(&default_varray)); } else { - component.attribute_try_create(attribute_name, domain, data_type, AttributeInitDefault()); + component.attribute_try_create(attribute_id, domain, data_type, AttributeInitDefault()); } - attribute = component.attribute_try_get_for_write(attribute_name); + attribute = component.attribute_try_get_for_write(attribute_id); if (!attribute) { /* Can't create the attribute. */ return {}; @@ -1202,28 +1276,88 @@ static blender::bke::OutputAttribute create_output_attribute( else { /* Fill the temporary array with values from the existing attribute. */ GVArrayPtr old_varray = component.attribute_get_for_read( - attribute_name, domain, data_type, default_value); + attribute_id, domain, data_type, default_value); old_varray->materialize_to_uninitialized(IndexRange(domain_size), data); } GVMutableArrayPtr varray = std::make_unique( - GMutableSpan{*cpp_type, data, domain_size}, component, attribute_name); + GMutableSpan{*cpp_type, data, domain_size}, component, attribute_id); return OutputAttribute(std::move(varray), domain, save_output_attribute, true); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output( - const StringRef attribute_name, - const AttributeDomain domain, - const CustomDataType data_type, - const void *default_value) +OutputAttribute GeometryComponent::attribute_try_get_for_output(const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const CustomDataType data_type, + const void *default_value) { - return create_output_attribute(*this, attribute_name, domain, data_type, false, default_value); + return create_output_attribute(*this, attribute_id, domain, data_type, false, default_value); } -blender::bke::OutputAttribute GeometryComponent::attribute_try_get_for_output_only( - const blender::StringRef attribute_name, +OutputAttribute GeometryComponent::attribute_try_get_for_output_only( + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type) { - return create_output_attribute(*this, attribute_name, domain, data_type, true, nullptr); + return create_output_attribute(*this, attribute_id, domain, data_type, true, nullptr); +} + +namespace blender::bke { + +const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type); + return scope.add(std::move(attribute), __func__); + } + return nullptr; +} + +uint64_t AttributeFieldInput::hash() const +{ + return get_default_hash_2(name_, type_); +} + +bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AttributeFieldInput *other_typed = dynamic_cast(&other)) { + return name_ == other_typed->name_ && type_ == other_typed->type_; + } + return false; +} + +const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( + const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); + GVArrayPtr attribute = component.attribute_try_get_for_read( + anonymous_id_.get(), domain, data_type); + return scope.add(std::move(attribute), __func__); + } + return nullptr; +} + +uint64_t AnonymousAttributeFieldInput::hash() const +{ + return get_default_hash_2(anonymous_id_.get(), type_); } + +bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + if (const AnonymousAttributeFieldInput *other_typed = + dynamic_cast(&other)) { + return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_; + } + return false; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index b3a795faa30..261cb26d4e5 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -116,12 +116,13 @@ class BuiltinAttributeProvider { class DynamicAttributesProvider { public: virtual ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const = 0; + const AttributeIDRef &attribute_id) const = 0; virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const = 0; - virtual bool try_delete(GeometryComponent &component, const StringRef attribute_name) const = 0; + const AttributeIDRef &attribute_id) const = 0; + virtual bool try_delete(GeometryComponent &component, + const AttributeIDRef &attribute_id) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), - const StringRef UNUSED(attribute_name), + const AttributeIDRef &UNUSED(attribute_id), const AttributeDomain UNUSED(domain), const CustomDataType UNUSED(data_type), const AttributeInit &UNUSED(initializer)) const @@ -154,15 +155,15 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final; @@ -231,10 +232,10 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { } ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final; - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final; + const AttributeIDRef &attribute_id) const final; + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final; bool foreach_attribute(const GeometryComponent &component, const AttributeForeachCallback callback) const final; void foreach_domain(const FunctionRef callback) const final; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 5c18f6f3807..1c4f9c5a6ab 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -25,6 +25,7 @@ #include "DNA_curve_types.h" +#include "BKE_anonymous_attribute.hh" #include "BKE_curve.h" #include "BKE_spline.hh" @@ -37,6 +38,7 @@ using blender::MutableSpan; using blender::Span; using blender::StringRefNull; using blender::Vector; +using blender::bke::AttributeIDRef; blender::Span CurveEval::splines() const { @@ -330,13 +332,13 @@ void CurveEval::assert_valid_point_attributes() const return; } const int layer_len = splines_.first()->attributes.data.totlayer; - Map map; + Map map; for (const SplinePtr &spline : splines_) { BLI_assert(spline->attributes.data.totlayer == layer_len); spline->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { map.add_or_modify( - name, + attribute_id, [&](AttributeMetaData *map_data) { /* All unique attribute names should be added on the first spline. */ BLI_assert(spline == splines_.first()); diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 1a3200a9b6c..ad2d5d267d5 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -46,6 +46,7 @@ #include "BLT_translation.h" +#include "BKE_anonymous_attribute.h" #include "BKE_customdata.h" #include "BKE_customdata_file.h" #include "BKE_deform.h" @@ -2127,6 +2128,11 @@ bool CustomData_merge(const struct CustomData *source, if (flag & CD_FLAG_NOCOPY) { continue; } + if (layer->anonymous_id && + !BKE_anonymous_attribute_id_has_strong_references(layer->anonymous_id)) { + /* This attribute is not referenced anymore, so it can be treated as if it didn't exist. */ + continue; + } if (!(mask & CD_TYPE_AS_MASK(type))) { continue; } @@ -2166,6 +2172,11 @@ bool CustomData_merge(const struct CustomData *source, newlayer->active_mask = lastmask; newlayer->flag |= flag & (CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY); changed = true; + + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id); + newlayer->anonymous_id = layer->anonymous_id; + } } } @@ -2206,6 +2217,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, int totelem) { const LayerTypeInfo *typeInfo; + if (layer->anonymous_id != NULL) { + BKE_anonymous_attribute_id_decrement_weak(layer->anonymous_id); + layer->anonymous_id = NULL; + } if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) { typeInfo = layerType_getInfo(layer->type); @@ -2649,6 +2664,27 @@ void *CustomData_add_layer_named(CustomData *data, return NULL; } +void *CustomData_add_layer_anonymous(struct CustomData *data, + int type, + eCDAllocType alloctype, + void *layerdata, + int totelem, + const AnonymousAttributeID *anonymous_id) +{ + const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); + CustomDataLayer *layer = customData_add_layer__internal( + data, type, alloctype, layerdata, totelem, name); + CustomData_update_typemap(data); + + if (layer == NULL) { + return NULL; + } + + BKE_anonymous_attribute_id_increment_weak(anonymous_id); + layer->anonymous_id = anonymous_id; + return layer->data; +} + bool CustomData_free_layer(CustomData *data, int type, int totelem, int index) { const int index_first = CustomData_get_layer_index(data, type); @@ -2812,6 +2848,20 @@ void *CustomData_duplicate_referenced_layer_named(CustomData *data, return customData_duplicate_referenced_layer_index(data, layer_index, totelem); } +void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, + const int UNUSED(type), + const AnonymousAttributeID *anonymous_id, + const int totelem) +{ + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].anonymous_id == anonymous_id) { + return customData_duplicate_referenced_layer_index(data, i, totelem); + } + } + BLI_assert_unreachable(); + return NULL; +} + void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) { for (int i = 0; i < data->totlayer; i++) { @@ -4244,7 +4294,8 @@ void CustomData_blend_write_prepare(CustomData *data, for (i = 0, j = 0; i < totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; - if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */ + /* Layers with this flag set are not written to file. */ + if ((layer->flag & CD_FLAG_NOCOPY) || layer->anonymous_id != NULL) { data->totlayer--; // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 0b6ba966974..a24b60ee288 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -892,7 +892,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { const CurveEval *curve = get_curve_from_component_for_read(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -902,13 +902,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { Span splines = curve->splines(); Vector spans; /* GSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional first_span = splines[0]->attributes.get_for_read(attribute_name); + std::optional first_span = splines[0]->attributes.get_for_read(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional span = splines[i]->attributes.get_for_read(attribute_name); + std::optional span = splines[i]->attributes.get_for_read(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -945,7 +945,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* This function is almost the same as #try_get_for_read, but without const. */ WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr || curve->splines().size() == 0) { @@ -955,13 +955,13 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { MutableSpan splines = curve->splines(); Vector spans; /* GMutableSpan has no default constructor. */ spans.reserve(splines.size()); - std::optional first_span = splines[0]->attributes.get_for_write(attribute_name); + std::optional first_span = splines[0]->attributes.get_for_write(attribute_id); if (!first_span) { return {}; } spans.append(*first_span); for (const int i : IndexRange(1, splines.size() - 1)) { - std::optional span = splines[i]->attributes.get_for_write(attribute_name); + std::optional span = splines[i]->attributes.get_for_write(attribute_id); if (!span) { /* All splines should have the same set of data layers. It would be possible to recover * here and return partial data instead, but that would add a lot of complexity for a @@ -996,7 +996,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return attribute; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { @@ -1006,7 +1006,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ bool layer_freed = false; for (SplinePtr &spline : curve->splines()) { - layer_freed = spline->attributes.remove(attribute_name); + layer_freed = spline->attributes.remove(attribute_id); } return layer_freed; } @@ -1034,7 +1034,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { } bool try_create(GeometryComponent &component, - const StringRef attribute_name, + const AttributeIDRef &attribute_id, const AttributeDomain domain, const CustomDataType data_type, const AttributeInit &initializer) const final @@ -1053,7 +1053,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* First check the one case that allows us to avoid copying the input data. */ if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { void *source_data = static_cast(initializer).data; - if (!splines[0]->attributes.create_by_move(attribute_name, data_type, source_data)) { + if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { MEM_freeN(source_data); return false; } @@ -1062,7 +1062,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { /* Otherwise just create a custom data layer on each of the splines. */ for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_name, data_type)) { + if (!splines[i]->attributes.create(attribute_id, data_type)) { /* If attribute creation fails on one of the splines, we cannot leave the custom data * layers in the previous splines around, so delete them before returning. However, * this is not an expected case. */ @@ -1076,7 +1076,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { return true; } - WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_name); + WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); /* We just created the attribute, it should exist. */ BLI_assert(write_attribute); diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index ef93a3f9b3f..9a4b8f4eb92 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -818,16 +818,20 @@ class VArray_For_VertexWeights final : public VArray { class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } const MeshComponent &mesh_component = static_cast(component); const Mesh *mesh = mesh_component.get_for_read(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -843,17 +847,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { } WriteAttributeLookup try_get_for_write(GeometryComponent &component, - const StringRef attribute_name) const final + const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return {}; + } MeshComponent &mesh_component = static_cast(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return {}; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -872,17 +880,21 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { ATTR_DOMAIN_POINT}; } - bool try_delete(GeometryComponent &component, const StringRef attribute_name) const final + bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); + if (!attribute_id.is_named()) { + return false; + } MeshComponent &mesh_component = static_cast(component); Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return true; } + const std::string name = attribute_id.name(); const int vertex_group_index = BLI_findstringindex( - &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + &mesh->vertex_group_names, name.c_str(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return false; } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 376792b9b96..0e19324a3c1 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -331,7 +331,7 @@ void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, void geometry_set_gather_instances_attribute_info(Span set_groups, Span component_types, const Set &ignored_attributes, - Map &r_attributes) + Map &r_attributes) { for (const GeometryInstanceGroup &set_group : set_groups) { const GeometrySet &set = set_group.geometry_set; @@ -341,23 +341,24 @@ void geometry_set_gather_instances_attribute_info(Span se } const GeometryComponent &component = *set.get_component_for_read(component_type); - component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(name)) { - return true; - } - auto add_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; - attribute_kind->data_type = meta_data.data_type; - }; - auto modify_info = [&](AttributeKind *attribute_kind) { - attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ - attribute_kind->data_type = bke::attribute_data_type_highest_complexity( - {attribute_kind->data_type, meta_data.data_type}); - }; - - r_attributes.add_or_modify(name, add_info, modify_info); - return true; - }); + component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + return true; + } + auto add_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; + attribute_kind->data_type = meta_data.data_type; + }; + auto modify_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; /* TODO: Use highest priority domain. */ + attribute_kind->data_type = bke::attribute_data_type_highest_complexity( + {attribute_kind->data_type, meta_data.data_type}); + }; + + r_attributes.add_or_modify(attribute_id, add_info, modify_info); + return true; + }); } } } @@ -512,11 +513,11 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups, Span component_types, - const Map &attribute_info, + const Map &attribute_info, GeometryComponent &result) { - for (Map::Item entry : attribute_info.items()) { - StringRef name = entry.key; + for (Map::Item entry : attribute_info.items()) { + const AttributeIDRef attribute_id = entry.key; const AttributeDomain domain_output = entry.value.domain; const CustomDataType data_type_output = entry.value.data_type; const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(data_type_output); @@ -524,7 +525,7 @@ static void join_attributes(Span set_groups, result.attribute_try_create( entry.key, domain_output, data_type_output, AttributeInitDefault()); - WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(name); + WriteAttributeLookup write_attribute = result.attribute_try_get_for_write(attribute_id); if (!write_attribute || &write_attribute.varray->type() != cpp_type || write_attribute.domain != domain_output) { continue; @@ -543,7 +544,7 @@ static void join_attributes(Span set_groups, continue; /* Domain size is 0, so no need to increment the offset. */ } GVArrayPtr source_attribute = component.attribute_try_get_for_read( - name, domain_output, data_type_output); + attribute_id, domain_output, data_type_output); if (source_attribute) { fn::GVArray_GSpan src_span{*source_attribute}; @@ -653,7 +654,7 @@ static void join_instance_groups_mesh(Span set_groups, } /* Don't copy attributes that are stored directly in the mesh data structs. */ - Map attributes; + Map attributes; geometry_set_gather_instances_attribute_info( set_groups, component_types, @@ -674,7 +675,7 @@ static void join_instance_groups_pointcloud(Span set_grou PointCloudComponent &dst_component = result.get_component_for_write(); dst_component.replace(new_pointcloud); - Map attributes; + Map attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_POINT_CLOUD}, {"position"}, attributes); join_attributes(set_groups, @@ -708,7 +709,7 @@ static void join_instance_groups_curve(Span set_groups, G CurveComponent &dst_component = result.get_component_for_write(); dst_component.replace(curve); - Map attributes; + Map attributes; geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_CURVE}, diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index be4fd7aac4f..950026b9d65 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5140,6 +5140,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); + register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); @@ -5174,7 +5175,10 @@ static void registerGeometryNodes() register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); + register_node_type_geo_input_index(); register_node_type_geo_input_material(); + register_node_type_geo_input_normal(); + register_node_type_geo_input_position(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); @@ -5202,6 +5206,7 @@ static void registerGeometryNodes() register_node_type_geo_select_by_handle_type(); register_node_type_geo_select_by_material(); register_node_type_geo_separate_components(); + register_node_type_geo_set_position(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); register_node_type_geo_transform(); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 12815c2c7e9..f5fef5e4486 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -45,15 +45,20 @@ namespace blender::ed::spreadsheet { void GeometryDataSource::foreach_default_column_ids( FunctionRef fn) const { - component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != domain_) { - return true; - } - SpreadsheetColumnID column_id; - column_id.name = (char *)name.c_str(); - fn(column_id); - return true; - }); + component_->attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != domain_) { + return true; + } + if (attribute_id.is_anonymous()) { + return true; + } + SpreadsheetColumnID column_id; + std::string name = attribute_id.name(); + column_id.name = (char *)name.c_str(); + fn(column_id); + return true; + }); } std::unique_ptr GeometryDataSource::get_column_values( diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index f8d2acc74a8..3c27e9d5e19 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -28,14 +28,20 @@ set(INC_SYS set(SRC intern/cpp_types.cc + intern/field.cc intern/generic_vector_array.cc intern/generic_virtual_array.cc intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc + intern/multi_function_procedure.cc + intern/multi_function_procedure_builder.cc + intern/multi_function_procedure_executor.cc FN_cpp_type.hh FN_cpp_type_make.hh + FN_field.hh + FN_field_cpp_type.hh FN_generic_pointer.hh FN_generic_span.hh FN_generic_value_map.hh @@ -48,6 +54,9 @@ set(SRC FN_multi_function_data_type.hh FN_multi_function_param_type.hh FN_multi_function_params.hh + FN_multi_function_procedure.hh + FN_multi_function_procedure_builder.hh + FN_multi_function_procedure_executor.hh FN_multi_function_signature.hh ) @@ -60,8 +69,10 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) set(TEST_SRC tests/FN_cpp_type_test.cc + tests/FN_field_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc + tests/FN_multi_function_procedure_test.cc tests/FN_multi_function_test.cc ) set(TEST_LIB diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh new file mode 100644 index 00000000000..25188531580 --- /dev/null +++ b/source/blender/functions/FN_field.hh @@ -0,0 +1,456 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + * + * A #Field represents a function that outputs a value based on an arbitrary number of inputs. The + * inputs for a specific field evaluation are provided by a #FieldContext. + * + * A typical example is a field that computes a displacement vector for every vertex on a mesh + * based on its position. + * + * Fields can be build, composed and evaluated at run-time. They are stored in a directed tree + * graph data structure, whereby each node is a #FieldNode and edges are dependencies. A #FieldNode + * has an arbitrary number of inputs and at least one output and a #Field references a specific + * output of a #FieldNode. The inputs of a #FieldNode are other fields. + * + * There are two different types of field nodes: + * - #FieldInput: Has no input and exactly one output. It represents an input to the entire field + * when it is evaluated. During evaluation, the value of this input is based on a #FieldContext. + * - #FieldOperation: Has an arbitrary number of field inputs and at least one output. Its main + * use is to compose multiple existing fields into new fields. + * + * When fields are evaluated, they are converted into a multi-function procedure which allows + * efficient compution. In the future, we might support different field evaluation mechanisms for + * e.g. the following scenarios: + * - Latency of a single evaluation is more important than throughput. + * - Evaluation should happen on other hardware like GPUs. + * + * Whenever possible, multiple fields should be evaluated together to avoid duplicate work when + * they share common sub-fields and a common context. + */ + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "FN_generic_virtual_array.hh" +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure.hh" +#include "FN_multi_function_procedure_builder.hh" +#include "FN_multi_function_procedure_executor.hh" + +namespace blender::fn { + +/** + * A node in a field-tree. It has at least one output that can be referenced by fields. + */ +class FieldNode { + private: + bool is_input_; + + public: + FieldNode(bool is_input) : is_input_(is_input) + { + } + + ~FieldNode() = default; + + virtual const CPPType &output_cpp_type(int output_index) const = 0; + + bool is_input() const + { + return is_input_; + } + + bool is_operation() const + { + return !is_input_; + } + + virtual uint64_t hash() const + { + return get_default_hash(this); + } + + friend bool operator==(const FieldNode &a, const FieldNode &b) + { + return a.is_equal_to(b); + } + + virtual bool is_equal_to(const FieldNode &other) const + { + return this == &other; + } +}; + +/** + * Common base class for fields to avoid declaring the same methods for #GField and #GFieldRef. + */ +template class GFieldBase { + protected: + NodePtr node_ = nullptr; + int node_output_index_ = 0; + + GFieldBase(NodePtr node, const int node_output_index) + : node_(std::move(node)), node_output_index_(node_output_index) + { + } + + public: + GFieldBase() = default; + + operator bool() const + { + return node_ != nullptr; + } + + friend bool operator==(const GFieldBase &a, const GFieldBase &b) + { + return &*a.node_ == &*b.node_ && a.node_output_index_ == b.node_output_index_; + } + + uint64_t hash() const + { + return get_default_hash_2(node_, node_output_index_); + } + + const fn::CPPType &cpp_type() const + { + return node_->output_cpp_type(node_output_index_); + } + + const FieldNode &node() const + { + return *node_; + } + + int node_output_index() const + { + return node_output_index_; + } +}; + +/** + * A field whose output type is only known at run-time. + */ +class GField : public GFieldBase> { + public: + GField() = default; + + GField(std::shared_ptr node, const int node_output_index = 0) + : GFieldBase>(std::move(node), node_output_index) + { + } +}; + +/** + * Same as #GField but is cheaper to copy/move around, because it does not contain a + * #std::shared_ptr. + */ +class GFieldRef : public GFieldBase { + public: + GFieldRef() = default; + + GFieldRef(const GField &field) + : GFieldBase(&field.node(), field.node_output_index()) + { + } + + GFieldRef(const FieldNode &node, const int node_output_index = 0) + : GFieldBase(&node, node_output_index) + { + } +}; + +/** + * A typed version of #GField. It has the same memory layout as #GField. + */ +template class Field : public GField { + public: + Field() = default; + + Field(GField field) : GField(std::move(field)) + { + BLI_assert(this->cpp_type().template is()); + } + + Field(std::shared_ptr node, const int node_output_index = 0) + : Field(GField(std::move(node), node_output_index)) + { + } +}; + +/** + * A #FieldNode that allows composing existing fields into new fields. + */ +class FieldOperation : public FieldNode { + /** + * The multi-function used by this node. It is optionally owned. + * Multi-functions with mutable or vector parameters are not supported currently. + */ + std::unique_ptr owned_function_; + const MultiFunction *function_; + + /** Inputs to the operation. */ + blender::Vector inputs_; + + public: + FieldOperation(std::unique_ptr function, Vector inputs = {}) + : FieldNode(false), owned_function_(std::move(function)), inputs_(std::move(inputs)) + { + function_ = owned_function_.get(); + } + + FieldOperation(const MultiFunction &function, Vector inputs = {}) + : FieldNode(false), function_(&function), inputs_(std::move(inputs)) + { + } + + Span inputs() const + { + return inputs_; + } + + const MultiFunction &multi_function() const + { + return *function_; + } + + const CPPType &output_cpp_type(int output_index) const override + { + int output_counter = 0; + for (const int param_index : function_->param_indices()) { + MFParamType param_type = function_->param_type(param_index); + if (param_type.is_output()) { + if (output_counter == output_index) { + return param_type.data_type().single_type(); + } + output_counter++; + } + } + BLI_assert_unreachable(); + return CPPType::get(); + } +}; + +class FieldContext; + +/** + * A #FieldNode that represents an input to the entire field-tree. + */ +class FieldInput : public FieldNode { + protected: + const CPPType *type_; + std::string debug_name_; + + public: + FieldInput(const CPPType &type, std::string debug_name = "") + : FieldNode(true), type_(&type), debug_name_(std::move(debug_name)) + { + } + + /** + * Get the value of this specific input based on the given context. The returned virtual array, + * should live at least as long as the passed in #scope. May return null. + */ + virtual const GVArray *get_varray_for_context(const FieldContext &context, + IndexMask mask, + ResourceScope &scope) const = 0; + + blender::StringRef debug_name() const + { + return debug_name_; + } + + const CPPType &cpp_type() const + { + return *type_; + } + + const CPPType &output_cpp_type(int output_index) const override + { + BLI_assert(output_index == 0); + UNUSED_VARS_NDEBUG(output_index); + return *type_; + } +}; + +/** + * Provides inputs for a specific field evaluation. + */ +class FieldContext { + public: + ~FieldContext() = default; + + virtual const GVArray *get_varray_for_input(const FieldInput &field_input, + IndexMask mask, + ResourceScope &scope) const; +}; + +/** + * Utility class that makes it easier to evaluate fields. + */ +class FieldEvaluator : NonMovable, NonCopyable { + private: + struct OutputPointerInfo { + void *dst = nullptr; + /* When a destination virtual array is provided for an input, this is + * unnecessary, otherwise this is used to construct the required virtual array. */ + void (*set)(void *dst, const GVArray &varray, ResourceScope &scope) = nullptr; + }; + + ResourceScope scope_; + const FieldContext &context_; + const IndexMask mask_; + Vector fields_to_evaluate_; + Vector dst_varrays_; + Vector evaluated_varrays_; + Vector output_pointer_infos_; + bool is_evaluated_ = false; + + public: + /** Takes #mask by pointer because the mask has to live longer than the evaluator. */ + FieldEvaluator(const FieldContext &context, const IndexMask *mask) + : context_(context), mask_(*mask) + { + } + + /** Construct a field evaluator for all indices less than #size. */ + FieldEvaluator(const FieldContext &context, const int64_t size) : context_(context), mask_(size) + { + } + + ~FieldEvaluator() + { + /* While this assert isn't strictly necessary, and could be replaced with a warning, + * it will catch cases where someone forgets to call #evaluate(). */ + BLI_assert(is_evaluated_); + } + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable virtual array that the evaluated result for this field is be written into. + */ + int add_with_destination(GField field, GVMutableArray &dst); + + /** Same as #add_with_destination but typed. */ + template int add_with_destination(Field field, VMutableArray &dst) + { + GVMutableArray &varray = scope_.construct>(__func__, dst); + return this->add_with_destination(GField(std::move(field)), varray); + } + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable span that the evaluated result for this field is be written into. + * \note: When the output may only be used as a single value, the version of this function with + * a virtual array result array should be used. + */ + int add_with_destination(GField field, GMutableSpan dst); + + /** + * \param field: Field to add to the evaluator. + * \param dst: Mutable span that the evaluated result for this field is be written into. + * \note: When the output may only be used as a single value, the version of this function with + * a virtual array result array should be used. + */ + template int add_with_destination(Field field, MutableSpan dst) + { + GVMutableArray &varray = scope_.construct>(__func__, dst); + return this->add_with_destination(std::move(field), varray); + } + + int add(GField field, const GVArray **varray_ptr); + + /** + * \param field: Field to add to the evaluator. + * \param varray_ptr: Once #evaluate is called, the resulting virtual array will be will be + * assigned to the given position. + * \return Index of the field in the evaluator which can be used in the #get_evaluated methods. + */ + template int add(Field field, const VArray **varray_ptr) + { + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append(OutputPointerInfo{ + varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &scope) { + *(const VArray **)dst = &*scope.construct>(__func__, varray); + }}); + return field_index; + } + + /** + * \return Index of the field in the evaluator which can be used in the #get_evaluated methods. + */ + int add(GField field); + + /** + * Evaluate all fields on the evaluator. This can only be called once. + */ + void evaluate(); + + const GVArray &get_evaluated(const int field_index) const + { + BLI_assert(is_evaluated_); + return *evaluated_varrays_[field_index]; + } + + template const VArray &get_evaluated(const int field_index) + { + const GVArray &varray = this->get_evaluated(field_index); + GVArray_Typed &typed_varray = scope_.construct>(__func__, varray); + return *typed_varray; + } + + /** + * Retrieve the output of an evaluated boolean field and convert it to a mask, which can be used + * to avoid calculations for unnecessary elements later on. The evaluator will own the indices in + * some cases, so it must live at least as long as the returned mask. + */ + IndexMask get_evaluated_as_mask(const int field_index); +}; + +Vector evaluate_fields(ResourceScope &scope, + Span fields_to_evaluate, + IndexMask mask, + const FieldContext &context, + Span dst_varrays = {}); + +/* -------------------------------------------------------------------- + * Utility functions for simple field creation and evaluation. + */ + +void evaluate_constant_field(const GField &field, void *r_value); + +template T evaluate_constant_field(const Field &field) +{ + T value; + value.~T(); + evaluate_constant_field(field, &value); + return value; +} + +template Field make_constant_field(T value) +{ + auto constant_fn = std::make_unique>(std::forward(value)); + auto operation = std::make_shared(std::move(constant_fn)); + return Field{GField{std::move(operation), 0}}; +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh new file mode 100644 index 00000000000..5e6f1b5a585 --- /dev/null +++ b/source/blender/functions/FN_field_cpp_type.hh @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "FN_cpp_type_make.hh" +#include "FN_field.hh" + +namespace blender::fn { + +template struct FieldCPPTypeParam { +}; + +class FieldCPPType : public CPPType { + private: + const CPPType &field_type_; + + public: + template + FieldCPPType(FieldCPPTypeParam> /* unused */, StringRef debug_name) + : CPPType(CPPTypeParam, CPPTypeFlags::None>(), debug_name), + field_type_(CPPType::get()) + { + } + + const CPPType &field_type() const + { + return field_type_; + } + + /* Ensure that #GField and #Field have the same layout, to enable casting between the two. */ + static_assert(sizeof(Field) == sizeof(GField)); + static_assert(sizeof(Field) == sizeof(Field)); + + const GField &get_gfield(const void *field) const + { + return *(const GField *)field; + } + + void construct_from_gfield(void *r_value, const GField &gfield) const + { + new (r_value) GField(gfield); + } +}; + +} // namespace blender::fn + +#define MAKE_FIELD_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \ + template<> \ + const blender::fn::CPPType &blender::fn::CPPType::get_impl>() \ + { \ + static blender::fn::FieldCPPType cpp_type{ \ + blender::fn::FieldCPPTypeParam>(), STRINGIFY(DEBUG_NAME)}; \ + return cpp_type; \ + } diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 7a526bb640b..d13615ced07 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -417,4 +417,13 @@ class CustomMF_DefaultOutput : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext context) const override; }; +class CustomMF_GenericCopy : public MultiFunction { + private: + MFSignature signature_; + + public: + CustomMF_GenericCopy(StringRef name, MFDataType data_type); + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index a480287d578..5af86c7c284 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -195,7 +195,7 @@ class MFParams { template const VArray &readonly_single_input(int param_index, StringRef name = "") { const GVArray &array = this->readonly_single_input(param_index, name); - return builder_->scope_.construct>(__func__, array); + return builder_->scope_.construct>(__func__, array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh new file mode 100644 index 00000000000..62f2292c1d9 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -0,0 +1,452 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "FN_multi_function.hh" + +namespace blender::fn { + +class MFVariable; +class MFInstruction; +class MFCallInstruction; +class MFBranchInstruction; +class MFDestructInstruction; +class MFDummyInstruction; +class MFReturnInstruction; +class MFProcedure; + +/** Every instruction has exactly one of these types. */ +enum class MFInstructionType { + Call, + Branch, + Destruct, + Dummy, + Return, +}; + +/** + * A variable is similar to a virtual register in other libraries. During evaluation, every is + * either uninitialized or contains a value for every index (remember, a multi-function procedure + * is always evaluated for many indices at the same time). + */ +class MFVariable : NonCopyable, NonMovable { + private: + MFDataType data_type_; + Vector users_; + std::string name_; + int id_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + + public: + MFDataType data_type() const; + Span users(); + + StringRefNull name() const; + void set_name(std::string name); + + int id() const; +}; + +/** Base class for all instruction types. */ +class MFInstruction : NonCopyable, NonMovable { + protected: + MFInstructionType type_; + Vector prev_; + + friend MFProcedure; + friend MFCallInstruction; + friend MFBranchInstruction; + friend MFDestructInstruction; + friend MFDummyInstruction; + friend MFReturnInstruction; + + public: + MFInstructionType type() const; + + /** + * Other instructions that come before this instruction. There can be multiple previous + * instructions when branching is used in the procedure. + */ + Span prev(); + Span prev() const; +}; + +/** + * References a multi-function that is evaluated when the instruction is executed. It also + * references the variables whose data will be passed into the multi-function. + */ +class MFCallInstruction : public MFInstruction { + private: + const MultiFunction *fn_ = nullptr; + MFInstruction *next_ = nullptr; + MutableSpan params_; + + friend MFProcedure; + + public: + const MultiFunction &fn() const; + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); + + void set_param_variable(int param_index, MFVariable *variable); + void set_params(Span variables); + + Span params(); + Span params() const; +}; + +/** + * What makes a branch instruction special is that it has two successor instructions. One that will + * be used when a condition variable was true, and one otherwise. + */ +class MFBranchInstruction : public MFInstruction { + private: + MFVariable *condition_ = nullptr; + MFInstruction *branch_true_ = nullptr; + MFInstruction *branch_false_ = nullptr; + + friend MFProcedure; + + public: + MFVariable *condition(); + const MFVariable *condition() const; + void set_condition(MFVariable *variable); + + MFInstruction *branch_true(); + const MFInstruction *branch_true() const; + void set_branch_true(MFInstruction *instruction); + + MFInstruction *branch_false(); + const MFInstruction *branch_false() const; + void set_branch_false(MFInstruction *instruction); +}; + +/** + * A destruct instruction destructs a single variable. So the variable value will be uninitialized + * after this instruction. All variables that are not output variables of the procedure, have to be + * destructed before the procedure ends. Destructing early is generally a good thing, because it + * might help with memory buffer reuse, which decreases memory-usage and increases performance. + */ +class MFDestructInstruction : public MFInstruction { + private: + MFVariable *variable_ = nullptr; + MFInstruction *next_ = nullptr; + + friend MFProcedure; + + public: + MFVariable *variable(); + const MFVariable *variable() const; + void set_variable(MFVariable *variable); + + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +/** + * This instruction does nothing, it just exists to building a procedure simpler in some cases. + */ +class MFDummyInstruction : public MFInstruction { + private: + MFInstruction *next_ = nullptr; + + friend MFProcedure; + + public: + MFInstruction *next(); + const MFInstruction *next() const; + void set_next(MFInstruction *instruction); +}; + +/** + * This instruction ends the procedure. + */ +class MFReturnInstruction : public MFInstruction { +}; + +/** + * Inputs and outputs of the entire procedure network. + */ +struct MFParameter { + MFParamType::InterfaceType type; + MFVariable *variable; +}; + +struct ConstMFParameter { + MFParamType::InterfaceType type; + const MFVariable *variable; +}; + +/** + * A multi-function procedure allows composing multi-functions in arbitrary ways. It consists of + * variables and instructions that operate on those variables. Branching and looping within the + * procedure is supported as well. + * + * Typically, a #MFProcedure should be constructed using a #MFProcedureBuilder, which has many more + * utility methods for common use cases. + */ +class MFProcedure : NonCopyable, NonMovable { + private: + LinearAllocator<> allocator_; + Vector call_instructions_; + Vector branch_instructions_; + Vector destruct_instructions_; + Vector dummy_instructions_; + Vector return_instructions_; + Vector variables_; + Vector params_; + MFInstruction *entry_ = nullptr; + + friend class MFProcedureDotExport; + + public: + MFProcedure() = default; + ~MFProcedure(); + + MFVariable &new_variable(MFDataType data_type, std::string name = ""); + MFCallInstruction &new_call_instruction(const MultiFunction &fn); + MFBranchInstruction &new_branch_instruction(); + MFDestructInstruction &new_destruct_instruction(); + MFDummyInstruction &new_dummy_instruction(); + MFReturnInstruction &new_return_instruction(); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + + Span params() const; + + MFInstruction *entry(); + const MFInstruction *entry() const; + void set_entry(MFInstruction &entry); + + Span variables(); + Span variables() const; + + std::string to_dot() const; + + bool validate() const; + + private: + bool validate_all_instruction_pointers_set() const; + bool validate_all_params_provided() const; + bool validate_same_variables_in_one_call() const; + bool validate_parameters() const; + bool validate_initialization() const; + + struct InitState { + bool can_be_initialized = false; + bool can_be_uninitialized = false; + }; + + InitState find_initialization_state_before_instruction(const MFInstruction &target_instruction, + const MFVariable &variable) const; +}; + +namespace multi_function_procedure_types { +using MFVariable = fn::MFVariable; +using MFInstruction = fn::MFInstruction; +using MFCallInstruction = fn::MFCallInstruction; +using MFBranchInstruction = fn::MFBranchInstruction; +using MFDestructInstruction = fn::MFDestructInstruction; +using MFProcedure = fn::MFProcedure; +} // namespace multi_function_procedure_types + +/* -------------------------------------------------------------------- + * MFVariable inline methods. + */ + +inline MFDataType MFVariable::data_type() const +{ + return data_type_; +} + +inline Span MFVariable::users() +{ + return users_; +} + +inline StringRefNull MFVariable::name() const +{ + return name_; +} + +inline int MFVariable::id() const +{ + return id_; +} + +/* -------------------------------------------------------------------- + * MFInstruction inline methods. + */ + +inline MFInstructionType MFInstruction::type() const +{ + return type_; +} + +inline Span MFInstruction::prev() +{ + return prev_; +} + +inline Span MFInstruction::prev() const +{ + return prev_; +} + +/* -------------------------------------------------------------------- + * MFCallInstruction inline methods. + */ + +inline const MultiFunction &MFCallInstruction::fn() const +{ + return *fn_; +} + +inline MFInstruction *MFCallInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFCallInstruction::next() const +{ + return next_; +} + +inline Span MFCallInstruction::params() +{ + return params_; +} + +inline Span MFCallInstruction::params() const +{ + return params_; +} + +/* -------------------------------------------------------------------- + * MFBranchInstruction inline methods. + */ + +inline MFVariable *MFBranchInstruction::condition() +{ + return condition_; +} + +inline const MFVariable *MFBranchInstruction::condition() const +{ + return condition_; +} + +inline MFInstruction *MFBranchInstruction::branch_true() +{ + return branch_true_; +} + +inline const MFInstruction *MFBranchInstruction::branch_true() const +{ + return branch_true_; +} + +inline MFInstruction *MFBranchInstruction::branch_false() +{ + return branch_false_; +} + +inline const MFInstruction *MFBranchInstruction::branch_false() const +{ + return branch_false_; +} + +/* -------------------------------------------------------------------- + * MFDestructInstruction inline methods. + */ + +inline MFVariable *MFDestructInstruction::variable() +{ + return variable_; +} + +inline const MFVariable *MFDestructInstruction::variable() const +{ + return variable_; +} + +inline MFInstruction *MFDestructInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDestructInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFDummyInstruction inline methods. + */ + +inline MFInstruction *MFDummyInstruction::next() +{ + return next_; +} + +inline const MFInstruction *MFDummyInstruction::next() const +{ + return next_; +} + +/* -------------------------------------------------------------------- + * MFProcedure inline methods. + */ + +inline Span MFProcedure::params() const +{ + static_assert(sizeof(MFParameter) == sizeof(ConstMFParameter)); + return params_.as_span().cast(); +} + +inline MFInstruction *MFProcedure::entry() +{ + return entry_; +} + +inline const MFInstruction *MFProcedure::entry() const +{ + return entry_; +} + +inline Span MFProcedure::variables() +{ + return variables_; +} + +inline Span MFProcedure::variables() const +{ + return variables_; +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure_builder.hh b/source/blender/functions/FN_multi_function_procedure_builder.hh new file mode 100644 index 00000000000..d5e45470a0e --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_builder.hh @@ -0,0 +1,260 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "FN_multi_function_procedure.hh" + +namespace blender::fn { + +/** + * An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction + * can be inserted. + */ +class MFInstructionCursor { + private: + MFInstruction *instruction_ = nullptr; + /* Only used when it is a branch instruction. */ + bool branch_output_ = false; + /* Only used when instruction is null. */ + bool is_entry_ = false; + + public: + MFInstructionCursor() = default; + + MFInstructionCursor(MFCallInstruction &instruction); + MFInstructionCursor(MFDestructInstruction &instruction); + MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output); + MFInstructionCursor(MFDummyInstruction &instruction); + + static MFInstructionCursor Entry(); + + void insert(MFProcedure &procedure, MFInstruction *new_instruction); +}; + +/** + * Utility class to build a #MFProcedure. + */ +class MFProcedureBuilder { + private: + /** Procedure that is being build. */ + MFProcedure *procedure_ = nullptr; + /** Cursors where the next instruction should be inserted. */ + Vector cursors_; + + public: + struct Branch; + struct Loop; + + MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor = MFInstructionCursor::Entry()); + + MFProcedureBuilder(Span builders); + + MFProcedureBuilder(Branch &branch); + + void set_cursor(const MFInstructionCursor &cursor); + void set_cursor(Span cursors); + void set_cursor(Span builders); + void set_cursor_after_branch(Branch &branch); + void set_cursor_after_loop(Loop &loop); + + void add_destruct(MFVariable &variable); + void add_destruct(Span variables); + + MFReturnInstruction &add_return(); + + Branch add_branch(MFVariable &condition); + + Loop add_loop(); + void add_loop_continue(Loop &loop); + void add_loop_break(Loop &loop); + + MFCallInstruction &add_call_with_no_variables(const MultiFunction &fn); + MFCallInstruction &add_call_with_all_variables(const MultiFunction &fn, + Span param_variables); + + Vector add_call(const MultiFunction &fn, + Span input_and_mutable_variables = {}); + + template + std::array add_call(const MultiFunction &fn, + Span input_and_mutable_variables = {}); + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable); + MFVariable &add_parameter(MFParamType param_type, std::string name = ""); + + MFVariable &add_input_parameter(MFDataType data_type, std::string name = ""); + template MFVariable &add_single_input_parameter(std::string name = ""); + template MFVariable &add_single_mutable_parameter(std::string name = ""); + + void add_output_parameter(MFVariable &variable); + + private: + void link_to_cursors(MFInstruction *instruction); +}; + +struct MFProcedureBuilder::Branch { + MFProcedureBuilder branch_true; + MFProcedureBuilder branch_false; +}; + +struct MFProcedureBuilder::Loop { + MFInstruction *begin = nullptr; + MFDummyInstruction *end = nullptr; +}; + +/* -------------------------------------------------------------------- + * MFInstructionCursor inline methods. + */ + +inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction, + bool branch_output) + : instruction_(&instruction), branch_output_(branch_output) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction) + : instruction_(&instruction) +{ +} + +inline MFInstructionCursor MFInstructionCursor::Entry() +{ + MFInstructionCursor cursor; + cursor.is_entry_ = true; + return cursor; +} + +/* -------------------------------------------------------------------- + * MFProcedureBuilder inline methods. + */ + +inline MFProcedureBuilder::MFProcedureBuilder(Branch &branch) + : MFProcedureBuilder(*branch.branch_true.procedure_) +{ + this->set_cursor_after_branch(branch); +} + +inline MFProcedureBuilder::MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor) + : procedure_(&procedure), cursors_({initial_cursor}) +{ +} + +inline MFProcedureBuilder::MFProcedureBuilder(Span builders) + : MFProcedureBuilder(*builders[0]->procedure_) +{ + this->set_cursor(builders); +} + +inline void MFProcedureBuilder::set_cursor(const MFInstructionCursor &cursor) +{ + cursors_ = {cursor}; +} + +inline void MFProcedureBuilder::set_cursor(Span cursors) +{ + cursors_ = cursors; +} + +inline void MFProcedureBuilder::set_cursor_after_branch(Branch &branch) +{ + this->set_cursor({&branch.branch_false, &branch.branch_true}); +} + +inline void MFProcedureBuilder::set_cursor_after_loop(Loop &loop) +{ + this->set_cursor(MFInstructionCursor{*loop.end}); +} + +inline void MFProcedureBuilder::set_cursor(Span builders) +{ + cursors_.clear(); + for (MFProcedureBuilder *builder : builders) { + cursors_.extend(builder->cursors_); + } +} + +template +inline std::array MFProcedureBuilder::add_call( + const MultiFunction &fn, Span input_and_mutable_variables) +{ + Vector output_variables = this->add_call(fn, input_and_mutable_variables); + BLI_assert(output_variables.size() == OutputN); + + std::array output_array; + initialized_copy_n(output_variables.data(), OutputN, output_array.data()); + return output_array; +} + +inline void MFProcedureBuilder::add_parameter(MFParamType::InterfaceType interface_type, + MFVariable &variable) +{ + procedure_->add_parameter(interface_type, variable); +} + +inline MFVariable &MFProcedureBuilder::add_parameter(MFParamType param_type, std::string name) +{ + MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name)); + this->add_parameter(param_type.interface_type(), variable); + return variable; +} + +inline MFVariable &MFProcedureBuilder::add_input_parameter(MFDataType data_type, std::string name) +{ + return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name)); +} + +template +inline MFVariable &MFProcedureBuilder::add_single_input_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForSingleInput(CPPType::get()), std::move(name)); +} + +template +inline MFVariable &MFProcedureBuilder::add_single_mutable_parameter(std::string name) +{ + return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get()), std::move(name)); +} + +inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable) +{ + this->add_parameter(MFParamType::Output, variable); +} + +inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction) +{ + for (MFInstructionCursor &cursor : cursors_) { + cursor.insert(*procedure_, instruction); + } +} + +} // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_procedure_executor.hh b/source/blender/functions/FN_multi_function_procedure_executor.hh new file mode 100644 index 00000000000..9c8b59739b8 --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_executor.hh @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "FN_multi_function_procedure.hh" + +namespace blender::fn { + +/** A multi-function that executes a procedure internally. */ +class MFProcedureExecutor : public MultiFunction { + private: + MFSignature signature_; + const MFProcedure &procedure_; + + public: + MFProcedureExecutor(std::string name, const MFProcedure &procedure); + + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + +} // namespace blender::fn diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index 7be34d2a1bf..058fb76af2b 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -15,6 +15,7 @@ */ #include "FN_cpp_type_make.hh" +#include "FN_field_cpp_type.hh" #include "BLI_color.hh" #include "BLI_float2.hh" @@ -39,4 +40,12 @@ MAKE_CPP_TYPE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType MAKE_CPP_TYPE(string, std::string, CPPTypeFlags::BasicType) +MAKE_FIELD_CPP_TYPE(FloatField, float); +MAKE_FIELD_CPP_TYPE(Float2Field, float2); +MAKE_FIELD_CPP_TYPE(Float3Field, float3); +MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); +MAKE_FIELD_CPP_TYPE(BoolField, bool); +MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); +MAKE_FIELD_CPP_TYPE(StringField, std::string); + } // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc new file mode 100644 index 00000000000..fa7dea97b7f --- /dev/null +++ b/source/blender/functions/intern/field.cc @@ -0,0 +1,569 @@ +/* + * 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. + */ + +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector_set.hh" + +#include "FN_field.hh" + +namespace blender::fn { + +/* -------------------------------------------------------------------- + * Field Evaluation. + */ + +struct FieldTreeInfo { + /** + * When fields are built, they only have references to the fields that they depend on. This map + * allows traversal of fields in the opposite direction. So for every field it stores the other + * fields that depend on it directly. + */ + MultiValueMap field_users; + /** + * The same field input may exist in the field tree as as separate nodes due to the way + * the tree is constructed. This set contains every different input only once. + */ + VectorSet> deduplicated_field_inputs; +}; + +/** + * Collects some information from the field tree that is required by later steps. + */ +static FieldTreeInfo preprocess_field_tree(Span entry_fields) +{ + FieldTreeInfo field_tree_info; + + Stack fields_to_check; + Set handled_fields; + + for (GFieldRef field : entry_fields) { + if (handled_fields.add(field)) { + fields_to_check.push(field); + } + } + + while (!fields_to_check.is_empty()) { + GFieldRef field = fields_to_check.pop(); + if (field.node().is_input()) { + const FieldInput &field_input = static_cast(field.node()); + field_tree_info.deduplicated_field_inputs.add(field_input); + continue; + } + BLI_assert(field.node().is_operation()); + const FieldOperation &operation = static_cast(field.node()); + for (const GFieldRef operation_input : operation.inputs()) { + field_tree_info.field_users.add(operation_input, field); + if (handled_fields.add(operation_input)) { + fields_to_check.push(operation_input); + } + } + } + return field_tree_info; +} + +/** + * Retrieves the data from the context that is passed as input into the field. + */ +static Vector get_field_context_inputs( + ResourceScope &scope, + const IndexMask mask, + const FieldContext &context, + const Span> field_inputs) +{ + Vector field_context_inputs; + for (const FieldInput &field_input : field_inputs) { + const GVArray *varray = context.get_varray_for_input(field_input, mask, scope); + if (varray == nullptr) { + const CPPType &type = field_input.cpp_type(); + varray = &scope.construct( + __func__, type, mask.min_array_size(), type.default_value()); + } + field_context_inputs.append(varray); + } + return field_context_inputs; +} + +/** + * \return A set that contains all fields from the field tree that depend on an input that varies + * for different indices. + */ +static Set find_varying_fields(const FieldTreeInfo &field_tree_info, + Span field_context_inputs) +{ + Set found_fields; + Stack fields_to_check; + + /* The varying fields are the ones that depend on inputs that are not constant. Therefore we + * start the tree search at the non-constant input fields and traverse through all fields that + * depend on them. */ + for (const int i : field_context_inputs.index_range()) { + const GVArray *varray = field_context_inputs[i]; + if (varray->is_single()) { + continue; + } + const FieldInput &field_input = field_tree_info.deduplicated_field_inputs[i]; + const GFieldRef field_input_field{field_input, 0}; + const Span users = field_tree_info.field_users.lookup(field_input_field); + for (const GFieldRef &field : users) { + if (found_fields.add(field)) { + fields_to_check.push(field); + } + } + } + while (!fields_to_check.is_empty()) { + GFieldRef field = fields_to_check.pop(); + const Span users = field_tree_info.field_users.lookup(field); + for (GFieldRef field : users) { + if (found_fields.add(field)) { + fields_to_check.push(field); + } + } + } + return found_fields; +} + +/** + * Builds the #procedure so that it computes the the fields. + */ +static void build_multi_function_procedure_for_fields(MFProcedure &procedure, + ResourceScope &scope, + const FieldTreeInfo &field_tree_info, + Span output_fields) +{ + MFProcedureBuilder builder{procedure}; + /* Every input, intermediate and output field corresponds to a variable in the procedure. */ + Map variable_by_field; + + /* Start by adding the field inputs as parameters to the procedure. */ + for (const FieldInput &field_input : field_tree_info.deduplicated_field_inputs) { + MFVariable &variable = builder.add_input_parameter( + MFDataType::ForSingle(field_input.cpp_type()), field_input.debug_name()); + variable_by_field.add_new({field_input, 0}, &variable); + } + + /* Utility struct that is used to do proper depth first search traversal of the tree below. */ + struct FieldWithIndex { + GFieldRef field; + int current_input_index = 0; + }; + + for (GFieldRef field : output_fields) { + /* We start a new stack for each output field to make sure that a field pushed later to the + * stack does never depend on a field that was pushed before. */ + Stack fields_to_check; + fields_to_check.push({field, 0}); + while (!fields_to_check.is_empty()) { + FieldWithIndex &field_with_index = fields_to_check.peek(); + const GFieldRef &field = field_with_index.field; + if (variable_by_field.contains(field)) { + /* The field has been handled already. */ + fields_to_check.pop(); + continue; + } + /* Field inputs should already be handled above. */ + BLI_assert(field.node().is_operation()); + + const FieldOperation &operation = static_cast(field.node()); + const Span operation_inputs = operation.inputs(); + + if (field_with_index.current_input_index < operation_inputs.size()) { + /* Not all inputs are handled yet. Push the next input field to the stack and increment the + * input index. */ + fields_to_check.push({operation_inputs[field_with_index.current_input_index]}); + field_with_index.current_input_index++; + } + else { + /* All inputs variables are ready, now add the function call. */ + Vector input_variables; + for (const GField &field : operation_inputs) { + input_variables.append(variable_by_field.lookup(field)); + } + const MultiFunction &multi_function = operation.multi_function(); + Vector output_variables = builder.add_call(multi_function, input_variables); + /* Add newly created variables to the map. */ + for (const int i : output_variables.index_range()) { + variable_by_field.add_new({operation, i}, output_variables[i]); + } + } + } + } + + /* Add output parameters to the procedure. */ + Set already_output_variables; + for (const GFieldRef &field : output_fields) { + MFVariable *variable = variable_by_field.lookup(field); + if (!already_output_variables.add(variable)) { + /* One variable can be output at most once. To output the same value twice, we have to make + * a copy first. */ + const MultiFunction ©_fn = scope.construct( + __func__, "copy", variable->data_type()); + variable = builder.add_call<1>(copy_fn, {variable})[0]; + } + builder.add_output_parameter(*variable); + } + + /* Remove the variables that should not be destructed from the map. */ + for (const GFieldRef &field : output_fields) { + variable_by_field.remove(field); + } + /* Add destructor calls for the remaining variables. */ + for (MFVariable *variable : variable_by_field.values()) { + builder.add_destruct(*variable); + } + + builder.add_return(); + + // std::cout << procedure.to_dot() << "\n"; + BLI_assert(procedure.validate()); +} + +/** + * Utility class that destructs elements from a partially initialized array. + */ +struct PartiallyInitializedArray : NonCopyable, NonMovable { + void *buffer; + IndexMask mask; + const CPPType *type; + + ~PartiallyInitializedArray() + { + this->type->destruct_indices(this->buffer, this->mask); + } +}; + +/** + * Evaluate fields in the given context. If possible, multiple fields should be evaluated together, + * because that can be more efficient when they share common sub-fields. + * + * \param scope: The resource scope that owns data that makes up the output virtual arrays. Make + * sure the scope is not destructed when the output virtual arrays are still used. + * \param fields_to_evaluate: The fields that should be evaluated together. + * \param mask: Determines which indices are computed. The mask may be referenced by the returned + * virtual arrays. So the underlying indices (if applicable) should live longer then #scope. + * \param context: The context that the field is evaluated in. Used to retrieve data from each + * #FieldInput in the field network. + * \param dst_varrays: If provided, the computed data will be written into those virtual arrays + * instead of into newly created ones. That allows making the computed data live longer than + * #scope and is more efficient when the data will be written into those virtual arrays + * later anyway. + * \return The computed virtual arrays for each provided field. If #dst_varrays is passed, the + * provided virtual arrays are returned. + */ +Vector evaluate_fields(ResourceScope &scope, + Span fields_to_evaluate, + IndexMask mask, + const FieldContext &context, + Span dst_varrays) +{ + Vector r_varrays(fields_to_evaluate.size(), nullptr); + const int array_size = mask.min_array_size(); + + /* Destination arrays are optional. Create a small utility method to access them. */ + auto get_dst_varray_if_available = [&](int index) -> GVMutableArray * { + if (dst_varrays.is_empty()) { + return nullptr; + } + BLI_assert(dst_varrays[index] == nullptr || dst_varrays[index]->size() >= array_size); + return dst_varrays[index]; + }; + + /* Traverse the field tree and prepare some data that is used in later steps. */ + FieldTreeInfo field_tree_info = preprocess_field_tree(fields_to_evaluate); + + /* Get inputs that will be passed into the field when evaluated. */ + Vector field_context_inputs = get_field_context_inputs( + scope, mask, context, field_tree_info.deduplicated_field_inputs); + + /* Finish fields that output an input varray directly. For those we don't have to do any further + * processing. */ + for (const int out_index : fields_to_evaluate.index_range()) { + const GFieldRef &field = fields_to_evaluate[out_index]; + if (!field.node().is_input()) { + continue; + } + const FieldInput &field_input = static_cast(field.node()); + const int field_input_index = field_tree_info.deduplicated_field_inputs.index_of(field_input); + const GVArray *varray = field_context_inputs[field_input_index]; + r_varrays[out_index] = varray; + } + + Set varying_fields = find_varying_fields(field_tree_info, field_context_inputs); + + /* Separate fields into two categories. Those that are constant and need to be evaluated only + * once, and those that need to be evaluated for every index. */ + Vector varying_fields_to_evaluate; + Vector varying_field_indices; + Vector constant_fields_to_evaluate; + Vector constant_field_indices; + for (const int i : fields_to_evaluate.index_range()) { + if (r_varrays[i] != nullptr) { + /* Already done. */ + continue; + } + GFieldRef field = fields_to_evaluate[i]; + if (varying_fields.contains(field)) { + varying_fields_to_evaluate.append(field); + varying_field_indices.append(i); + } + else { + constant_fields_to_evaluate.append(field); + constant_field_indices.append(i); + } + } + + /* Evaluate varying fields if necessary. */ + if (!varying_fields_to_evaluate.is_empty()) { + /* Build the procedure for those fields. */ + MFProcedure procedure; + build_multi_function_procedure_for_fields( + procedure, scope, field_tree_info, varying_fields_to_evaluate); + MFProcedureExecutor procedure_executor{"Procedure", procedure}; + MFParamsBuilder mf_params{procedure_executor, array_size}; + MFContextBuilder mf_context; + + /* Provide inputs to the procedure executor. */ + for (const GVArray *varray : field_context_inputs) { + mf_params.add_readonly_single_input(*varray); + } + + for (const int i : varying_fields_to_evaluate.index_range()) { + const GFieldRef &field = varying_fields_to_evaluate[i]; + const CPPType &type = field.cpp_type(); + const int out_index = varying_field_indices[i]; + + /* Try to get an existing virtual array that the result should be written into. */ + GVMutableArray *output_varray = get_dst_varray_if_available(out_index); + void *buffer; + if (output_varray == nullptr || !output_varray->is_span()) { + /* Allocate a new buffer for the computed result. */ + buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment()); + + /* Make sure that elements in the buffer will be destructed. */ + PartiallyInitializedArray &destruct_helper = scope.construct( + __func__); + destruct_helper.buffer = buffer; + destruct_helper.mask = mask; + destruct_helper.type = &type; + + r_varrays[out_index] = &scope.construct( + __func__, GSpan{type, buffer, array_size}); + } + else { + /* Write the result into the existing span. */ + buffer = output_varray->get_internal_span().data(); + + r_varrays[out_index] = output_varray; + } + + /* Pass output buffer to the procedure executor. */ + const GMutableSpan span{type, buffer, array_size}; + mf_params.add_uninitialized_single_output(span); + } + + procedure_executor.call(mask, mf_params, mf_context); + } + + /* Evaluate constant fields if necessary. */ + if (!constant_fields_to_evaluate.is_empty()) { + /* Build the procedure for those fields. */ + MFProcedure procedure; + build_multi_function_procedure_for_fields( + procedure, scope, field_tree_info, constant_fields_to_evaluate); + MFProcedureExecutor procedure_executor{"Procedure", procedure}; + MFParamsBuilder mf_params{procedure_executor, 1}; + MFContextBuilder mf_context; + + /* Provide inputs to the procedure executor. */ + for (const GVArray *varray : field_context_inputs) { + mf_params.add_readonly_single_input(*varray); + } + + for (const int i : constant_fields_to_evaluate.index_range()) { + const GFieldRef &field = constant_fields_to_evaluate[i]; + const CPPType &type = field.cpp_type(); + /* Allocate memory where the computed value will be stored in. */ + void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment()); + + /* Use this to make sure that the value is destructed in the end. */ + PartiallyInitializedArray &destruct_helper = scope.construct( + __func__); + destruct_helper.buffer = buffer; + destruct_helper.mask = IndexRange(1); + destruct_helper.type = &type; + + /* Pass output buffer to the procedure executor. */ + mf_params.add_uninitialized_single_output({type, buffer, 1}); + + /* Create virtual array that can be used after the procedure has been executed below. */ + const int out_index = constant_field_indices[i]; + r_varrays[out_index] = &scope.construct( + __func__, type, array_size, buffer); + } + + procedure_executor.call(IndexRange(1), mf_params, mf_context); + } + + /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has + * written the computed data in the right place already. */ + if (!dst_varrays.is_empty()) { + for (const int out_index : fields_to_evaluate.index_range()) { + GVMutableArray *output_varray = get_dst_varray_if_available(out_index); + if (output_varray == nullptr) { + /* Caller did not provide a destination for this output. */ + continue; + } + const GVArray *computed_varray = r_varrays[out_index]; + BLI_assert(computed_varray->type() == output_varray->type()); + if (output_varray == computed_varray) { + /* The result has been written into the destination provided by the caller already. */ + continue; + } + /* Still have to copy over the data in the destination provided by the caller. */ + if (output_varray->is_span()) { + /* Materialize into a span. */ + computed_varray->materialize_to_uninitialized(output_varray->get_internal_span().data()); + } + else { + /* Slower materialize into a different structure. */ + const CPPType &type = computed_varray->type(); + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + for (const int i : mask) { + computed_varray->get_to_uninitialized(i, buffer); + output_varray->set_by_relocate(i, buffer); + } + } + r_varrays[out_index] = output_varray; + } + } + return r_varrays; +} + +void evaluate_constant_field(const GField &field, void *r_value) +{ + ResourceScope scope; + FieldContext context; + Vector varrays = evaluate_fields(scope, {field}, IndexRange(1), context); + varrays[0]->get_to_uninitialized(0, r_value); +} + +const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, + IndexMask mask, + ResourceScope &scope) const +{ + /* By default ask the field input to create the varray. Another field context might overwrite + * the context here. */ + return field_input.get_varray_for_context(*this, mask, scope); +} + +/* -------------------------------------------------------------------- + * FieldEvaluator. + */ + +static Vector indices_from_selection(const VArray &selection) +{ + /* If the selection is just a single value, it's best to avoid calling this + * function when constructing an IndexMask and use an IndexRange instead. */ + BLI_assert(!selection.is_single()); + + Vector indices; + if (selection.is_span()) { + Span span = selection.get_internal_span(); + for (const int64_t i : span.index_range()) { + if (span[i]) { + indices.append(i); + } + } + } + else { + for (const int i : selection.index_range()) { + if (selection[i]) { + indices.append(i); + } + } + } + return indices; +} + +int FieldEvaluator::add_with_destination(GField field, GVMutableArray &dst) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(&dst); + output_pointer_infos_.append({}); + return field_index; +} + +int FieldEvaluator::add_with_destination(GField field, GMutableSpan dst) +{ + GVMutableArray &varray = scope_.construct(__func__, dst); + return this->add_with_destination(std::move(field), varray); +} + +int FieldEvaluator::add(GField field, const GVArray **varray_ptr) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append(OutputPointerInfo{ + varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &UNUSED(scope)) { + *(const GVArray **)dst = &varray; + }}); + return field_index; +} + +int FieldEvaluator::add(GField field) +{ + const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); + dst_varrays_.append(nullptr); + output_pointer_infos_.append({}); + return field_index; +} + +void FieldEvaluator::evaluate() +{ + BLI_assert_msg(!is_evaluated_, "Cannot evaluate fields twice."); + Array fields(fields_to_evaluate_.size()); + for (const int i : fields_to_evaluate_.index_range()) { + fields[i] = fields_to_evaluate_[i]; + } + evaluated_varrays_ = evaluate_fields(scope_, fields, mask_, context_, dst_varrays_); + BLI_assert(fields_to_evaluate_.size() == evaluated_varrays_.size()); + for (const int i : fields_to_evaluate_.index_range()) { + OutputPointerInfo &info = output_pointer_infos_[i]; + if (info.dst != nullptr) { + info.set(info.dst, *evaluated_varrays_[i], scope_); + } + } + is_evaluated_ = true; +} + +IndexMask FieldEvaluator::get_evaluated_as_mask(const int field_index) +{ + const GVArray &varray = this->get_evaluated(field_index); + GVArray_Typed typed_varray{varray}; + + if (typed_varray->is_single()) { + if (typed_varray->get_internal_single()) { + return IndexRange(typed_varray.size()); + } + return IndexRange(0); + } + + return scope_.add_value(indices_from_selection(*typed_varray), __func__).as_span(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index c6b3b808130..180d1f17a54 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -123,4 +123,32 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU } } +CustomMF_GenericCopy::CustomMF_GenericCopy(StringRef name, MFDataType data_type) +{ + MFSignatureBuilder signature{name}; + signature.input("Input", data_type); + signature.output("Output", data_type); + signature_ = signature.build(); + this->set_signature(&signature_); +} + +void CustomMF_GenericCopy::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const +{ + const MFDataType data_type = this->param_type(0).data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const GVArray &inputs = params.readonly_single_input(0, "Input"); + GMutableSpan outputs = params.uninitialized_single_output(1, "Output"); + inputs.materialize_to_uninitialized(mask, outputs.data()); + break; + } + case MFDataType::Vector: { + const GVVectorArray &inputs = params.readonly_vector_input(0, "Input"); + GVectorArray &outputs = params.vector_output(1, "Output"); + outputs.extend(mask, inputs); + break; + } + } +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc new file mode 100644 index 00000000000..6eff7bc09f8 --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -0,0 +1,794 @@ +/* + * 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. + */ + +#include "FN_multi_function_procedure.hh" + +#include "BLI_dot_export.hh" +#include "BLI_stack.hh" + +namespace blender::fn { + +void MFVariable::set_name(std::string name) +{ + name_ = std::move(name); +} + +void MFCallInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable) +{ + if (params_[param_index] != nullptr) { + params_[param_index]->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type()); + variable->users_.append(this); + } + params_[param_index] = variable; +} + +void MFCallInstruction::set_params(Span variables) +{ + BLI_assert(variables.size() == params_.size()); + for (const int i : variables.index_range()) { + this->set_param_variable(i, variables[i]); + } +} + +void MFBranchInstruction::set_condition(MFVariable *variable) +{ + if (condition_ != nullptr) { + condition_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + condition_ = variable; +} + +void MFBranchInstruction::set_branch_true(MFInstruction *instruction) +{ + if (branch_true_ != nullptr) { + branch_true_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + branch_true_ = instruction; +} + +void MFBranchInstruction::set_branch_false(MFInstruction *instruction) +{ + if (branch_false_ != nullptr) { + branch_false_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + branch_false_ = instruction; +} + +void MFDestructInstruction::set_variable(MFVariable *variable) +{ + if (variable_ != nullptr) { + variable_->users_.remove_first_occurrence_and_reorder(this); + } + if (variable != nullptr) { + variable->users_.append(this); + } + variable_ = variable; +} + +void MFDestructInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +void MFDummyInstruction::set_next(MFInstruction *instruction) +{ + if (next_ != nullptr) { + next_->prev_.remove_first_occurrence_and_reorder(this); + } + if (instruction != nullptr) { + instruction->prev_.append(this); + } + next_ = instruction; +} + +MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name) +{ + MFVariable &variable = *allocator_.construct().release(); + variable.name_ = std::move(name); + variable.data_type_ = data_type; + variable.id_ = variables_.size(); + variables_.append(&variable); + return variable; +} + +MFCallInstruction &MFProcedure::new_call_instruction(const MultiFunction &fn) +{ + MFCallInstruction &instruction = *allocator_.construct().release(); + instruction.type_ = MFInstructionType::Call; + instruction.fn_ = &fn; + instruction.params_ = allocator_.allocate_array(fn.param_amount()); + instruction.params_.fill(nullptr); + call_instructions_.append(&instruction); + return instruction; +} + +MFBranchInstruction &MFProcedure::new_branch_instruction() +{ + MFBranchInstruction &instruction = *allocator_.construct().release(); + instruction.type_ = MFInstructionType::Branch; + branch_instructions_.append(&instruction); + return instruction; +} + +MFDestructInstruction &MFProcedure::new_destruct_instruction() +{ + MFDestructInstruction &instruction = *allocator_.construct().release(); + instruction.type_ = MFInstructionType::Destruct; + destruct_instructions_.append(&instruction); + return instruction; +} + +MFDummyInstruction &MFProcedure::new_dummy_instruction() +{ + MFDummyInstruction &instruction = *allocator_.construct().release(); + instruction.type_ = MFInstructionType::Dummy; + dummy_instructions_.append(&instruction); + return instruction; +} + +MFReturnInstruction &MFProcedure::new_return_instruction() +{ + MFReturnInstruction &instruction = *allocator_.construct().release(); + instruction.type_ = MFInstructionType::Return; + return_instructions_.append(&instruction); + return instruction; +} + +void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable) +{ + params_.append({interface_type, &variable}); +} + +void MFProcedure::set_entry(MFInstruction &entry) +{ + entry_ = &entry; +} + +MFProcedure::~MFProcedure() +{ + for (MFCallInstruction *instruction : call_instructions_) { + instruction->~MFCallInstruction(); + } + for (MFBranchInstruction *instruction : branch_instructions_) { + instruction->~MFBranchInstruction(); + } + for (MFDestructInstruction *instruction : destruct_instructions_) { + instruction->~MFDestructInstruction(); + } + for (MFDummyInstruction *instruction : dummy_instructions_) { + instruction->~MFDummyInstruction(); + } + for (MFReturnInstruction *instruction : return_instructions_) { + instruction->~MFReturnInstruction(); + } + for (MFVariable *variable : variables_) { + variable->~MFVariable(); + } +} + +bool MFProcedure::validate() const +{ + if (entry_ == nullptr) { + return false; + } + if (!this->validate_all_instruction_pointers_set()) { + return false; + } + if (!this->validate_all_params_provided()) { + return false; + } + if (!this->validate_same_variables_in_one_call()) { + return false; + } + if (!this->validate_parameters()) { + return false; + } + if (!this->validate_initialization()) { + return false; + } + return true; +} + +bool MFProcedure::validate_all_instruction_pointers_set() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + for (const MFDestructInstruction *instruction : destruct_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + if (instruction->branch_true_ == nullptr) { + return false; + } + if (instruction->branch_false_ == nullptr) { + return false; + } + } + for (const MFDummyInstruction *instruction : dummy_instructions_) { + if (instruction->next_ == nullptr) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_all_params_provided() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + for (const MFVariable *variable : instruction->params_) { + if (variable == nullptr) { + return false; + } + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + if (instruction->condition_ == nullptr) { + return false; + } + } + for (const MFDestructInstruction *instruction : destruct_instructions_) { + if (instruction->variable_ == nullptr) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_same_variables_in_one_call() const +{ + for (const MFCallInstruction *instruction : call_instructions_) { + const MultiFunction &fn = *instruction->fn_; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = instruction->params_[param_index]; + for (const int other_param_index : fn.param_indices()) { + if (other_param_index == param_index) { + continue; + } + const MFVariable *other_variable = instruction->params_[other_param_index]; + if (other_variable != variable) { + continue; + } + if (ELEM(param_type.interface_type(), MFParamType::Mutable, MFParamType::Output)) { + /* When a variable is used as mutable or output parameter, it can only be used once. */ + return false; + } + const MFParamType other_param_type = fn.param_type(other_param_index); + /* A variable is allowed to be used as input more than once. */ + if (other_param_type.interface_type() != MFParamType::Input) { + return false; + } + } + } + } + return true; +} + +bool MFProcedure::validate_parameters() const +{ + Set variables; + for (const MFParameter ¶m : params_) { + /* One variable cannot be used as multiple parameters. */ + if (!variables.add(param.variable)) { + return false; + } + } + return true; +} + +bool MFProcedure::validate_initialization() const +{ + /* TODO: Issue warning when it maybe wrongly initialized. */ + for (const MFDestructInstruction *instruction : destruct_instructions_) { + const MFVariable &variable = *instruction->variable_; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + if (!state.can_be_initialized) { + return false; + } + } + for (const MFBranchInstruction *instruction : branch_instructions_) { + const MFVariable &variable = *instruction->condition_; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + if (!state.can_be_initialized) { + return false; + } + } + for (const MFCallInstruction *instruction : call_instructions_) { + const MultiFunction &fn = *instruction->fn_; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable &variable = *instruction->params_[param_index]; + const InitState state = this->find_initialization_state_before_instruction(*instruction, + variable); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + if (!state.can_be_initialized) { + return false; + } + break; + } + case MFParamType::Output: { + if (!state.can_be_uninitialized) { + return false; + } + break; + } + } + } + } + Set variables_that_should_be_initialized_on_return; + for (const MFParameter ¶m : params_) { + if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) { + variables_that_should_be_initialized_on_return.add_new(param.variable); + } + } + for (const MFReturnInstruction *instruction : return_instructions_) { + for (const MFVariable *variable : variables_) { + const InitState init_state = this->find_initialization_state_before_instruction(*instruction, + *variable); + if (variables_that_should_be_initialized_on_return.contains(variable)) { + if (!init_state.can_be_initialized) { + return false; + } + } + else { + if (!init_state.can_be_uninitialized) { + return false; + } + } + } + } + return true; +} + +MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction( + const MFInstruction &target_instruction, const MFVariable &target_variable) const +{ + InitState state; + + auto check_entry_instruction = [&]() { + bool caller_initialized_variable = false; + for (const MFParameter ¶m : params_) { + if (param.variable == &target_variable) { + if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) { + caller_initialized_variable = true; + break; + } + } + } + if (caller_initialized_variable) { + state.can_be_initialized = true; + } + else { + state.can_be_uninitialized = true; + } + }; + + if (&target_instruction == entry_) { + check_entry_instruction(); + } + + Set checked_instructions; + Stack instructions_to_check; + instructions_to_check.push_multiple(target_instruction.prev_); + + while (!instructions_to_check.is_empty()) { + const MFInstruction &instruction = *instructions_to_check.pop(); + if (!checked_instructions.add(&instruction)) { + /* Skip if the instruction has been checked already. */ + continue; + } + bool state_modified = false; + switch (instruction.type_) { + case MFInstructionType::Call: { + const MFCallInstruction &call_instruction = static_cast( + instruction); + const MultiFunction &fn = *call_instruction.fn_; + for (const int param_index : fn.param_indices()) { + if (call_instruction.params_[param_index] == &target_variable) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.interface_type() == MFParamType::Output) { + state.can_be_initialized = true; + state_modified = true; + break; + } + } + } + break; + } + case MFInstructionType::Destruct: { + const MFDestructInstruction &destruct_instruction = + static_cast(instruction); + if (destruct_instruction.variable_ == &target_variable) { + state.can_be_uninitialized = true; + state_modified = true; + } + break; + } + case MFInstructionType::Branch: + case MFInstructionType::Dummy: + case MFInstructionType::Return: { + /* These instruction types don't change the initialization state of variables. */ + break; + } + } + + if (!state_modified) { + if (&instruction == entry_) { + check_entry_instruction(); + } + instructions_to_check.push_multiple(instruction.prev_); + } + } + + return state; +} + +class MFProcedureDotExport { + private: + const MFProcedure &procedure_; + dot::DirectedGraph digraph_; + Map dot_nodes_by_begin_; + Map dot_nodes_by_end_; + + public: + MFProcedureDotExport(const MFProcedure &procedure) : procedure_(procedure) + { + } + + std::string generate() + { + this->create_nodes(); + this->create_edges(); + return digraph_.to_dot_string(); + } + + void create_nodes() + { + Vector all_instructions; + auto add_instructions = [&](auto instructions) { + all_instructions.extend(instructions.begin(), instructions.end()); + }; + add_instructions(procedure_.call_instructions_); + add_instructions(procedure_.branch_instructions_); + add_instructions(procedure_.destruct_instructions_); + add_instructions(procedure_.dummy_instructions_); + add_instructions(procedure_.return_instructions_); + + Set handled_instructions; + + for (const MFInstruction *representative : all_instructions) { + if (handled_instructions.contains(representative)) { + continue; + } + Vector block_instructions = this->get_instructions_in_block( + *representative); + std::stringstream ss; + ss << "<"; + + for (const MFInstruction *current : block_instructions) { + handled_instructions.add_new(current); + switch (current->type()) { + case MFInstructionType::Call: { + this->instruction_to_string(*static_cast(current), ss); + break; + } + case MFInstructionType::Destruct: { + this->instruction_to_string(*static_cast(current), ss); + break; + } + case MFInstructionType::Dummy: { + this->instruction_to_string(*static_cast(current), ss); + break; + } + case MFInstructionType::Return: { + this->instruction_to_string(*static_cast(current), ss); + break; + } + case MFInstructionType::Branch: { + this->instruction_to_string(*static_cast(current), ss); + break; + } + } + ss << R"(
)"; + } + ss << ">"; + + dot::Node &dot_node = digraph_.new_node(ss.str()); + dot_node.set_shape(dot::Attr_shape::Rectangle); + dot_nodes_by_begin_.add_new(block_instructions.first(), &dot_node); + dot_nodes_by_end_.add_new(block_instructions.last(), &dot_node); + } + } + + void create_edges() + { + auto create_edge = [&](dot::Node &from_node, + const MFInstruction *to_instruction) -> dot::DirectedEdge & { + if (to_instruction == nullptr) { + dot::Node &to_node = digraph_.new_node("missing"); + to_node.set_shape(dot::Attr_shape::Diamond); + return digraph_.new_edge(from_node, to_node); + } + dot::Node &to_node = *dot_nodes_by_begin_.lookup(to_instruction); + return digraph_.new_edge(from_node, to_node); + }; + + for (auto item : dot_nodes_by_end_.items()) { + const MFInstruction &from_instruction = *item.key; + dot::Node &from_node = *item.value; + switch (from_instruction.type()) { + case MFInstructionType::Call: { + const MFInstruction *to_instruction = + static_cast(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Destruct: { + const MFInstruction *to_instruction = + static_cast(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Dummy: { + const MFInstruction *to_instruction = + static_cast(from_instruction).next(); + create_edge(from_node, to_instruction); + break; + } + case MFInstructionType::Return: { + break; + } + case MFInstructionType::Branch: { + const MFBranchInstruction &branch_instruction = static_cast( + from_instruction); + const MFInstruction *to_true_instruction = branch_instruction.branch_true(); + const MFInstruction *to_false_instruction = branch_instruction.branch_false(); + create_edge(from_node, to_true_instruction).attributes.set("color", "#118811"); + create_edge(from_node, to_false_instruction).attributes.set("color", "#881111"); + break; + } + } + } + + dot::Node &entry_node = this->create_entry_node(); + create_edge(entry_node, procedure_.entry()); + } + + bool has_to_be_block_begin(const MFInstruction &instruction) + { + if (procedure_.entry() == &instruction) { + return true; + } + if (instruction.prev().size() != 1) { + return true; + } + if (instruction.prev()[0]->type() == MFInstructionType::Branch) { + return true; + } + return false; + } + + const MFInstruction &get_first_instruction_in_block(const MFInstruction &representative) + { + const MFInstruction *current = &representative; + while (!this->has_to_be_block_begin(*current)) { + current = current->prev()[0]; + if (current == &representative) { + /* There is a loop without entry or exit, just break it up here. */ + break; + } + } + return *current; + } + + const MFInstruction *get_next_instruction_in_block(const MFInstruction &instruction, + const MFInstruction &block_begin) + { + const MFInstruction *next = nullptr; + switch (instruction.type()) { + case MFInstructionType::Call: { + next = static_cast(instruction).next(); + break; + } + case MFInstructionType::Destruct: { + next = static_cast(instruction).next(); + break; + } + case MFInstructionType::Dummy: { + next = static_cast(instruction).next(); + break; + } + case MFInstructionType::Return: + case MFInstructionType::Branch: { + break; + } + } + if (next == nullptr) { + return nullptr; + } + if (next == &block_begin) { + return nullptr; + } + if (this->has_to_be_block_begin(*next)) { + return nullptr; + } + return next; + } + + Vector get_instructions_in_block(const MFInstruction &representative) + { + Vector instructions; + const MFInstruction &begin = this->get_first_instruction_in_block(representative); + for (const MFInstruction *current = &begin; current != nullptr; + current = this->get_next_instruction_in_block(*current, begin)) { + instructions.append(current); + } + return instructions; + } + + void variable_to_string(const MFVariable *variable, std::stringstream &ss) + { + if (variable == nullptr) { + ss << "null"; + } + else { + ss << "$" << variable->id(); + if (!variable->name().is_empty()) { + ss << "(" << variable->name() << ")"; + } + } + } + + void instruction_name_format(StringRef name, std::stringstream &ss) + { + ss << name; + } + + void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss) + { + const MultiFunction &fn = instruction.fn(); + this->instruction_name_format(fn.name() + ": ", ss); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = instruction.params()[param_index]; + ss << R"()"; + switch (param_type.interface_type()) { + case MFParamType::Input: { + ss << "in"; + break; + } + case MFParamType::Mutable: { + ss << "mut"; + break; + } + case MFParamType::Output: { + ss << "out"; + break; + } + } + ss << " "; + variable_to_string(variable, ss); + if (param_index < fn.param_amount() - 1) { + ss << ", "; + } + } + } + + void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss) + { + instruction_name_format("Destruct ", ss); + variable_to_string(instruction.variable(), ss); + } + + void instruction_to_string(const MFDummyInstruction &UNUSED(instruction), std::stringstream &ss) + { + instruction_name_format("Dummy ", ss); + } + + void instruction_to_string(const MFReturnInstruction &UNUSED(instruction), std::stringstream &ss) + { + instruction_name_format("Return ", ss); + + Vector outgoing_parameters; + for (const ConstMFParameter ¶m : procedure_.params()) { + if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) { + outgoing_parameters.append(param); + } + } + for (const int param_index : outgoing_parameters.index_range()) { + const ConstMFParameter ¶m = outgoing_parameters[param_index]; + variable_to_string(param.variable, ss); + if (param_index < outgoing_parameters.size() - 1) { + ss << ", "; + } + } + } + + void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss) + { + instruction_name_format("Branch ", ss); + variable_to_string(instruction.condition(), ss); + } + + dot::Node &create_entry_node() + { + std::stringstream ss; + ss << "Entry: "; + Vector incoming_parameters; + for (const ConstMFParameter ¶m : procedure_.params()) { + if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) { + incoming_parameters.append(param); + } + } + for (const int param_index : incoming_parameters.index_range()) { + const ConstMFParameter ¶m = incoming_parameters[param_index]; + variable_to_string(param.variable, ss); + if (param_index < incoming_parameters.size() - 1) { + ss << ", "; + } + } + + dot::Node &node = digraph_.new_node(ss.str()); + node.set_shape(dot::Attr_shape::Ellipse); + return node; + } +}; + +std::string MFProcedure::to_dot() const +{ + MFProcedureDotExport dot_export{*this}; + return dot_export.generate(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_builder.cc b/source/blender/functions/intern/multi_function_procedure_builder.cc new file mode 100644 index 00000000000..3c088776bea --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_builder.cc @@ -0,0 +1,175 @@ +/* + * 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. + */ + +#include "FN_multi_function_procedure_builder.hh" + +namespace blender::fn { + +void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction) +{ + if (instruction_ == nullptr) { + if (is_entry_) { + procedure.set_entry(*new_instruction); + } + else { + /* The cursors points at nothing, nothing to do. */ + } + } + else { + switch (instruction_->type()) { + case MFInstructionType::Call: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Branch: { + MFBranchInstruction &branch_instruction = *static_cast( + instruction_); + if (branch_output_) { + branch_instruction.set_branch_true(new_instruction); + } + else { + branch_instruction.set_branch_false(new_instruction); + } + break; + } + case MFInstructionType::Destruct: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Dummy: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Return: { + /* It shouldn't be possible to build a cursor that points to a return instruction. */ + BLI_assert_unreachable(); + break; + } + } + } +} + +void MFProcedureBuilder::add_destruct(MFVariable &variable) +{ + MFDestructInstruction &instruction = procedure_->new_destruct_instruction(); + instruction.set_variable(&variable); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; +} + +void MFProcedureBuilder::add_destruct(Span variables) +{ + for (MFVariable *variable : variables) { + this->add_destruct(*variable); + } +} + +MFReturnInstruction &MFProcedureBuilder::add_return() +{ + MFReturnInstruction &instruction = procedure_->new_return_instruction(); + this->link_to_cursors(&instruction); + cursors_ = {}; + return instruction; +} + +MFCallInstruction &MFProcedureBuilder::add_call_with_no_variables(const MultiFunction &fn) +{ + MFCallInstruction &instruction = procedure_->new_call_instruction(fn); + this->link_to_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; + return instruction; +} + +MFCallInstruction &MFProcedureBuilder::add_call_with_all_variables( + const MultiFunction &fn, Span param_variables) +{ + MFCallInstruction &instruction = this->add_call_with_no_variables(fn); + instruction.set_params(param_variables); + return instruction; +} + +Vector MFProcedureBuilder::add_call(const MultiFunction &fn, + Span input_and_mutable_variables) +{ + Vector output_variables; + MFCallInstruction &instruction = this->add_call_with_no_variables(fn); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + MFVariable *variable = input_and_mutable_variables.first(); + instruction.set_param_variable(param_index, variable); + input_and_mutable_variables = input_and_mutable_variables.drop_front(1); + break; + } + case MFParamType::Output: { + MFVariable &variable = procedure_->new_variable(param_type.data_type(), + fn.param_name(param_index)); + instruction.set_param_variable(param_index, &variable); + output_variables.append(&variable); + break; + } + } + } + /* All passed in variables should have been dropped in the loop above. */ + BLI_assert(input_and_mutable_variables.is_empty()); + return output_variables; +} + +MFProcedureBuilder::Branch MFProcedureBuilder::add_branch(MFVariable &condition) +{ + MFBranchInstruction &instruction = procedure_->new_branch_instruction(); + instruction.set_condition(&condition); + this->link_to_cursors(&instruction); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); + + Branch branch{*procedure_, *procedure_}; + branch.branch_true.set_cursor(MFInstructionCursor{instruction, true}); + branch.branch_false.set_cursor(MFInstructionCursor{instruction, false}); + return branch; +} + +MFProcedureBuilder::Loop MFProcedureBuilder::add_loop() +{ + MFDummyInstruction &loop_begin = procedure_->new_dummy_instruction(); + MFDummyInstruction &loop_end = procedure_->new_dummy_instruction(); + this->link_to_cursors(&loop_begin); + cursors_ = {MFInstructionCursor{loop_begin}}; + + Loop loop; + loop.begin = &loop_begin; + loop.end = &loop_end; + + return loop; +} + +void MFProcedureBuilder::add_loop_continue(Loop &loop) +{ + this->link_to_cursors(loop.begin); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +void MFProcedureBuilder::add_loop_break(Loop &loop) +{ + this->link_to_cursors(loop.end); + /* Clear cursors because this builder ends here. */ + cursors_.clear(); +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc new file mode 100644 index 00000000000..38b26415779 --- /dev/null +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -0,0 +1,1212 @@ +/* + * 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. + */ + +#include "FN_multi_function_procedure_executor.hh" + +#include "BLI_stack.hh" + +namespace blender::fn { + +MFProcedureExecutor::MFProcedureExecutor(std::string name, const MFProcedure &procedure) + : procedure_(procedure) +{ + MFSignatureBuilder signature(std::move(name)); + + for (const ConstMFParameter ¶m : procedure.params()) { + signature.add(param.variable->name(), MFParamType(param.type, param.variable->data_type())); + } + + signature_ = signature.build(); + this->set_signature(&signature_); +} + +using IndicesSplitVectors = std::array, 2>; + +namespace { +enum class ValueType { + GVArray = 0, + Span = 1, + GVVectorArray = 2, + GVectorArray = 3, + OneSingle = 4, + OneVector = 5, +}; +constexpr int tot_variable_value_types = 6; +} // namespace + +/** + * During evaluation, a variable may be stored in various different forms, depending on what + * instructions do with the variables. + */ +struct VariableValue { + ValueType type; + + VariableValue(ValueType type) : type(type) + { + } +}; + +/* This variable is the unmodified virtual array from the caller. */ +struct VariableValue_GVArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVArray; + const GVArray &data; + + VariableValue_GVArray(const GVArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different value for every index. Some values may be uninitialized. The span + * may be owned by the caller. */ +struct VariableValue_Span : public VariableValue { + static inline constexpr ValueType static_type = ValueType::Span; + void *data; + bool owned; + + VariableValue_Span(void *data, bool owned) : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable is the unmodified virtual vector array from the caller. */ +struct VariableValue_GVVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVVectorArray; + const GVVectorArray &data; + + VariableValue_GVVectorArray(const GVVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has a different vector for every index. */ +struct VariableValue_GVectorArray : public VariableValue { + static inline constexpr ValueType static_type = ValueType::GVectorArray; + GVectorArray &data; + bool owned; + + VariableValue_GVectorArray(GVectorArray &data, bool owned) + : VariableValue(static_type), data(data), owned(owned) + { + } +}; + +/* This variable has the same value for every index. */ +struct VariableValue_OneSingle : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneSingle; + void *data; + bool is_initialized = false; + + VariableValue_OneSingle(void *data) : VariableValue(static_type), data(data) + { + } +}; + +/* This variable has the same vector for every index. */ +struct VariableValue_OneVector : public VariableValue { + static inline constexpr ValueType static_type = ValueType::OneVector; + GVectorArray &data; + + VariableValue_OneVector(GVectorArray &data) : VariableValue(static_type), data(data) + { + } +}; + +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_destructible_v); +static_assert(std::is_trivially_destructible_v); + +class VariableState; + +/** + * The #ValueAllocator is responsible for providing memory for variables and their values. It also + * manages the reuse of buffers to improve performance. + */ +class ValueAllocator : NonCopyable, NonMovable { + private: + /* Allocate with 64 byte alignment for better reusability of buffers and improved cache + * performance. */ + static constexpr inline int min_alignment = 64; + + /* Use stacks so that the most recently used buffers are reused first. This improves cache + * efficiency. */ + std::array, tot_variable_value_types> values_free_lists_; + /* The integer key is the size of one element (e.g. 4 for an integer buffer). All buffers are + * aligned to #min_alignment bytes. */ + Map> span_buffers_free_list_; + + public: + ValueAllocator() = default; + + ~ValueAllocator() + { + for (Stack &stack : values_free_lists_) { + while (!stack.is_empty()) { + MEM_freeN(stack.pop()); + } + } + for (Stack &stack : span_buffers_free_list_.values()) { + while (!stack.is_empty()) { + MEM_freeN(stack.pop()); + } + } + } + + template VariableState *obtain_variable_state(Args &&...args); + + void release_variable_state(VariableState *state); + + VariableValue_GVArray *obtain_GVArray(const GVArray &varray) + { + return this->obtain(varray); + } + + VariableValue_GVVectorArray *obtain_GVVectorArray(const GVVectorArray &varray) + { + return this->obtain(varray); + } + + VariableValue_Span *obtain_Span_not_owned(void *buffer) + { + return this->obtain(buffer, false); + } + + VariableValue_Span *obtain_Span(const CPPType &type, int size) + { + void *buffer = nullptr; + + const int element_size = type.size(); + const int alignment = type.alignment(); + + if (alignment > min_alignment) { + /* In this rare case we fallback to not reusing existing buffers. */ + buffer = MEM_mallocN_aligned(element_size * size, alignment, __func__); + } + else { + Stack *stack = span_buffers_free_list_.lookup_ptr(element_size); + if (stack == nullptr || stack->is_empty()) { + buffer = MEM_mallocN_aligned(element_size * size, min_alignment, __func__); + } + else { + /* Reuse existing buffer. */ + buffer = stack->pop(); + } + } + + return this->obtain(buffer, true); + } + + VariableValue_GVectorArray *obtain_GVectorArray_not_owned(GVectorArray &data) + { + return this->obtain(data, false); + } + + VariableValue_GVectorArray *obtain_GVectorArray(const CPPType &type, int size) + { + GVectorArray *vector_array = new GVectorArray(type, size); + return this->obtain(*vector_array, true); + } + + VariableValue_OneSingle *obtain_OneSingle(const CPPType &type) + { + void *buffer = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + return this->obtain(buffer); + } + + VariableValue_OneVector *obtain_OneVector(const CPPType &type) + { + GVectorArray *vector_array = new GVectorArray(type, 1); + return this->obtain(*vector_array); + } + + void release_value(VariableValue *value, const MFDataType &data_type) + { + switch (value->type) { + case ValueType::GVArray: { + break; + } + case ValueType::Span: { + auto *value_typed = static_cast(value); + if (value_typed->owned) { + const CPPType &type = data_type.single_type(); + /* Assumes all values in the buffer are uninitialized already. */ + Stack &buffers = span_buffers_free_list_.lookup_or_add_default(type.size()); + buffers.push(value_typed->data); + } + break; + } + case ValueType::GVVectorArray: { + break; + } + case ValueType::GVectorArray: { + auto *value_typed = static_cast(value); + if (value_typed->owned) { + delete &value_typed->data; + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = static_cast(value); + if (value_typed->is_initialized) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + } + MEM_freeN(value_typed->data); + break; + } + case ValueType::OneVector: { + auto *value_typed = static_cast(value); + delete &value_typed->data; + break; + } + } + + Stack &stack = values_free_lists_[(int)value->type]; + stack.push(value); + } + + private: + template T *obtain(Args &&...args) + { + static_assert(std::is_base_of_v); + Stack &stack = values_free_lists_[(int)T::static_type]; + if (stack.is_empty()) { + void *buffer = MEM_mallocN(sizeof(T), __func__); + return new (buffer) T(std::forward(args)...); + } + return new (stack.pop()) T(std::forward(args)...); + } +}; + +/** + * This class keeps track of a single variable during evaluation. + */ +class VariableState : NonCopyable, NonMovable { + private: + /** The current value of the variable. The storage format may change over time. */ + VariableValue *value_; + /** Number of indices that are currently initialized in this variable. */ + int tot_initialized_; + /* This a non-owning pointer to either span buffer or #GVectorArray or null. */ + void *caller_provided_storage_ = nullptr; + + public: + VariableState(VariableValue &value, int tot_initialized, void *caller_provided_storage = nullptr) + : value_(&value), + tot_initialized_(tot_initialized), + caller_provided_storage_(caller_provided_storage) + { + } + + void destruct_self(ValueAllocator &value_allocator, const MFDataType &data_type) + { + value_allocator.release_value(value_, data_type); + value_allocator.release_variable_state(this); + } + + /* True if this contains only one value for all indices, i.e. the value for all indices is + * the same. */ + bool is_one() const + { + switch (value_->type) { + case ValueType::GVArray: + return this->value_as()->data.is_single(); + case ValueType::Span: + return tot_initialized_ == 0; + case ValueType::GVVectorArray: + return this->value_as()->data.is_single_vector(); + case ValueType::GVectorArray: + return tot_initialized_ == 0; + case ValueType::OneSingle: + return true; + case ValueType::OneVector: + return true; + } + BLI_assert_unreachable(); + return false; + } + + bool is_fully_initialized(const IndexMask full_mask) + { + return tot_initialized_ == full_mask.size(); + } + + bool is_fully_uninitialized(const IndexMask full_mask) + { + UNUSED_VARS(full_mask); + return tot_initialized_ == 0; + } + + void add_as_input(MFParamsBuilder ¶ms, IndexMask mask, const MFDataType &data_type) const + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as()->data); + break; + } + case ValueType::Span: { + const void *data = this->value_as()->data; + const GSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_readonly_single_input(span); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as()->data); + break; + } + case ValueType::GVectorArray: { + params.add_readonly_vector_input(this->value_as()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as(); + BLI_assert(value_typed->is_initialized); + const GPointer gpointer{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(gpointer); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as()->data[0]); + break; + } + } + } + + void ensure_is_mutable(IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + if (ELEM(value_->type, ValueType::Span, ValueType::GVectorArray)) { + return; + } + + const int array_size = full_mask.min_array_size(); + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_Span *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_Span(type, array_size); + } + else { + /* Reuse the storage provided caller when possible. */ + new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_); + } + if (value_->type == ValueType::GVArray) { + /* Fill new buffer with data from virtual array. */ + this->value_as()->data.materialize_to_uninitialized( + full_mask, new_value->data); + } + else if (value_->type == ValueType::OneSingle) { + auto *old_value_typed_ = this->value_as(); + if (old_value_typed_->is_initialized) { + /* Fill the buffer with a single value. */ + type.fill_construct_indices(old_value_typed_->data, new_value->data, full_mask); + } + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_GVectorArray *new_value = nullptr; + if (caller_provided_storage_ == nullptr) { + new_value = value_allocator.obtain_GVectorArray(type, array_size); + } + else { + new_value = value_allocator.obtain_GVectorArray_not_owned( + *(GVectorArray *)caller_provided_storage_); + } + if (value_->type == ValueType::GVVectorArray) { + /* Fill new vector array with data from virtual vector array. */ + new_value->data.extend(full_mask, this->value_as()->data); + } + else if (value_->type == ValueType::OneVector) { + /* Fill all indices with the same value. */ + const GSpan vector = this->value_as()->data[0]; + new_value->data.extend(full_mask, GVVectorArray_For_SingleGSpan{vector, array_size}); + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are initialized. */ + BLI_assert(mask.size() <= tot_initialized_); + + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_single_mutable(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_mutable(this->value_as()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output(MFParamsBuilder ¶ms, + IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + /* Sanity check to make sure that enough values are not initialized. */ + BLI_assert(mask.size() <= full_mask.size() - tot_initialized_); + this->ensure_is_mutable(full_mask, data_type, value_allocator); + + switch (value_->type) { + case ValueType::Span: { + void *data = this->value_as()->data; + const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()}; + params.add_uninitialized_single_output(span); + break; + } + case ValueType::GVectorArray: { + params.add_vector_output(this->value_as()->data); + break; + } + case ValueType::GVArray: + case ValueType::GVVectorArray: + case ValueType::OneSingle: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void add_as_input__one(MFParamsBuilder ¶ms, const MFDataType &data_type) const + { + BLI_assert(this->is_one()); + + switch (value_->type) { + case ValueType::GVArray: { + params.add_readonly_single_input(this->value_as()->data); + break; + } + case ValueType::GVVectorArray: { + params.add_readonly_vector_input(this->value_as()->data); + break; + } + case ValueType::OneSingle: { + const auto *value_typed = this->value_as(); + BLI_assert(value_typed->is_initialized); + GPointer ptr{data_type.single_type(), value_typed->data}; + params.add_readonly_single_input(ptr); + break; + } + case ValueType::OneVector: { + params.add_readonly_vector_input(this->value_as()->data); + break; + } + case ValueType::Span: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void ensure_is_mutable__one(const MFDataType &data_type, ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + if (ELEM(value_->type, ValueType::OneSingle, ValueType::OneVector)) { + return; + } + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + VariableValue_OneSingle *new_value = value_allocator.obtain_OneSingle(type); + if (value_->type == ValueType::GVArray) { + this->value_as()->data.get_internal_single_to_uninitialized( + new_value->data); + new_value->is_initialized = true; + } + else if (value_->type == ValueType::Span) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do, the single value is uninitialized already. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + VariableValue_OneVector *new_value = value_allocator.obtain_OneVector(type); + if (value_->type == ValueType::GVVectorArray) { + const GVVectorArray &old_vector_array = + this->value_as()->data; + new_value->data.extend(IndexRange(1), old_vector_array); + } + else if (value_->type == ValueType::GVectorArray) { + BLI_assert(tot_initialized_ == 0); + /* Nothing to do. */ + } + else { + BLI_assert_unreachable(); + } + value_allocator.release_value(value_, data_type); + value_ = new_value; + break; + } + } + } + + void add_as_mutable__one(MFParamsBuilder ¶ms, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as(); + BLI_assert(value_typed->is_initialized); + params.add_single_mutable(GMutableSpan{data_type.single_type(), value_typed->data, 1}); + break; + } + case ValueType::OneVector: { + params.add_vector_mutable(this->value_as()->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + } + + void add_as_output__one(MFParamsBuilder ¶ms, + IndexMask mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + BLI_assert(this->is_one()); + this->ensure_is_mutable__one(data_type, value_allocator); + + switch (value_->type) { + case ValueType::OneSingle: { + auto *value_typed = this->value_as(); + BLI_assert(!value_typed->is_initialized); + params.add_uninitialized_single_output( + GMutableSpan{data_type.single_type(), value_typed->data, 1}); + /* It becomes initialized when the multi-function is called. */ + value_typed->is_initialized = true; + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as(); + BLI_assert(value_typed->data[0].is_empty()); + params.add_vector_output(value_typed->data); + break; + } + case ValueType::GVArray: + case ValueType::Span: + case ValueType::GVVectorArray: + case ValueType::GVectorArray: { + BLI_assert_unreachable(); + break; + } + } + + tot_initialized_ += mask.size(); + } + + void destruct(IndexMask mask, + IndexMask full_mask, + const MFDataType &data_type, + ValueAllocator &value_allocator) + { + int new_tot_initialized = tot_initialized_ - mask.size(); + + /* Sanity check to make sure that enough indices can be destructed. */ + BLI_assert(new_tot_initialized >= 0); + + switch (value_->type) { + case ValueType::GVArray: { + if (mask.size() == full_mask.size()) { + /* All elements are destructed. The elements are owned by the caller, so we don't + * actually destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneSingle(data_type.single_type()); + } + else { + /* Not all elements are destructed. Since we can't work on the original array, we have to + * create a copy first. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::Span); + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as()->data, mask); + } + break; + } + case ValueType::Span: { + const CPPType &type = data_type.single_type(); + type.destruct_indices(this->value_as()->data, mask); + if (new_tot_initialized == 0) { + /* Release span when all values are initialized. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneSingle(data_type.single_type()); + } + break; + } + case ValueType::GVVectorArray: { + if (mask.size() == full_mask.size()) { + /* All elements are cleared. The elements are owned by the caller, so don't actually + * destruct them. */ + value_allocator.release_value(value_, data_type); + value_ = value_allocator.obtain_OneVector(data_type.vector_base_type()); + } + else { + /* Not all elements are cleared. Since we can't work on the original vector array, we + * have to create a copy first. A possible future optimization is to create the partial + * copy directly. */ + this->ensure_is_mutable(full_mask, data_type, value_allocator); + BLI_assert(value_->type == ValueType::GVectorArray); + this->value_as()->data.clear(mask); + } + break; + } + case ValueType::GVectorArray: { + this->value_as()->data.clear(mask); + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as(); + BLI_assert(value_typed->is_initialized); + if (mask.size() == tot_initialized_) { + const CPPType &type = data_type.single_type(); + type.destruct(value_typed->data); + value_typed->is_initialized = false; + } + break; + } + case ValueType::OneVector: { + auto *value_typed = this->value_as(); + if (mask.size() == tot_initialized_) { + value_typed->data.clear({0}); + } + break; + } + } + + tot_initialized_ = new_tot_initialized; + } + + void indices_split(IndexMask mask, IndicesSplitVectors &r_indices) + { + BLI_assert(mask.size() <= tot_initialized_); + + switch (value_->type) { + case ValueType::GVArray: { + const GVArray_Typed varray{this->value_as()->data}; + for (const int i : mask) { + r_indices[varray[i]].append(i); + } + break; + } + case ValueType::Span: { + const Span span((bool *)this->value_as()->data, + mask.min_array_size()); + for (const int i : mask) { + r_indices[span[i]].append(i); + } + break; + } + case ValueType::OneSingle: { + auto *value_typed = this->value_as(); + BLI_assert(value_typed->is_initialized); + const bool condition = *(bool *)value_typed->data; + r_indices[condition].extend(mask); + break; + } + case ValueType::GVVectorArray: + case ValueType::GVectorArray: + case ValueType::OneVector: { + BLI_assert_unreachable(); + break; + } + } + } + + template T *value_as() + { + BLI_assert(value_->type == T::static_type); + return static_cast(value_); + } + + template const T *value_as() const + { + BLI_assert(value_->type == T::static_type); + return static_cast(value_); + } +}; + +template VariableState *ValueAllocator::obtain_variable_state(Args &&...args) +{ + return new VariableState(std::forward(args)...); +} + +void ValueAllocator::release_variable_state(VariableState *state) +{ + delete state; +} + +/** Keeps track of the states of all variables during evaluation. */ +class VariableStates { + private: + ValueAllocator value_allocator_; + Map variable_states_; + IndexMask full_mask_; + + public: + VariableStates(IndexMask full_mask) : full_mask_(full_mask) + { + } + + ~VariableStates() + { + for (auto &&item : variable_states_.items()) { + const MFVariable *variable = item.key; + VariableState *state = item.value; + state->destruct_self(value_allocator_, variable->data_type()); + } + } + + ValueAllocator &value_allocator() + { + return value_allocator_; + } + + const IndexMask &full_mask() const + { + return full_mask_; + } + + void add_initial_variable_states(const MFProcedureExecutor &fn, + const MFProcedure &procedure, + MFParams ¶ms) + { + for (const int param_index : fn.param_indices()) { + MFParamType param_type = fn.param_type(param_index); + const MFVariable *variable = procedure.params()[param_index].variable; + + auto add_state = [&](VariableValue *value, + bool input_is_initialized, + void *caller_provided_storage = nullptr) { + const int tot_initialized = input_is_initialized ? full_mask_.size() : 0; + variable_states_.add_new(variable, + value_allocator_.obtain_variable_state( + *value, tot_initialized, caller_provided_storage)); + }; + + switch (param_type.category()) { + case MFParamType::SingleInput: { + const GVArray &data = params.readonly_single_input(param_index); + add_state(value_allocator_.obtain_GVArray(data), true); + break; + } + case MFParamType::VectorInput: { + const GVVectorArray &data = params.readonly_vector_input(param_index); + add_state(value_allocator_.obtain_GVVectorArray(data), true); + break; + } + case MFParamType::SingleOutput: { + GMutableSpan data = params.uninitialized_single_output(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data()); + break; + } + case MFParamType::VectorOutput: { + GVectorArray &data = params.vector_output(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data); + break; + } + case MFParamType::SingleMutable: { + GMutableSpan data = params.single_mutable(param_index); + add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data()); + break; + } + case MFParamType::VectorMutable: { + GVectorArray &data = params.vector_mutable(param_index); + add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data); + break; + } + } + } + } + + void add_as_param(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input(params, mask, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_); + break; + } + } + } + + void add_as_param__one(VariableState &variable_state, + MFParamsBuilder ¶ms, + const MFParamType ¶m_type, + const IndexMask &mask) + { + const MFDataType data_type = param_type.data_type(); + switch (param_type.interface_type()) { + case MFParamType::Input: { + variable_state.add_as_input__one(params, data_type); + break; + } + case MFParamType::Mutable: { + variable_state.add_as_mutable__one(params, data_type, value_allocator_); + break; + } + case MFParamType::Output: { + variable_state.add_as_output__one(params, mask, data_type, value_allocator_); + break; + } + } + } + + void destruct(const MFVariable &variable, const IndexMask &mask) + { + VariableState &variable_state = this->get_variable_state(variable); + variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_); + } + + VariableState &get_variable_state(const MFVariable &variable) + { + return *variable_states_.lookup_or_add_cb( + &variable, [&]() { return this->create_new_state_for_variable(variable); }); + } + + VariableState *create_new_state_for_variable(const MFVariable &variable) + { + MFDataType data_type = variable.data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &type = data_type.single_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneSingle(type), 0); + } + case MFDataType::Vector: { + const CPPType &type = data_type.vector_base_type(); + return value_allocator_.obtain_variable_state(*value_allocator_.obtain_OneVector(type), 0); + } + } + BLI_assert_unreachable(); + return nullptr; + } +}; + +static bool evaluate_as_one(const MultiFunction &fn, + Span param_variable_states, + const IndexMask &mask, + const IndexMask &full_mask) +{ + if (fn.depends_on_context()) { + return false; + } + if (mask.size() < full_mask.size()) { + return false; + } + for (VariableState *state : param_variable_states) { + if (!state->is_one()) { + return false; + } + } + return true; +} + +static void execute_call_instruction(const MFCallInstruction &instruction, + IndexMask mask, + VariableStates &variable_states, + const MFContext &context) +{ + const MultiFunction &fn = instruction.fn(); + + Vector param_variable_states; + param_variable_states.resize(fn.param_amount()); + + for (const int param_index : fn.param_indices()) { + const MFVariable *variable = instruction.params()[param_index]; + VariableState &variable_state = variable_states.get_variable_state(*variable); + param_variable_states[param_index] = &variable_state; + } + + /* If all inputs to the function are constant, it's enough to call the function only once instead + * of for every index. */ + if (evaluate_as_one(fn, param_variable_states, mask, variable_states.full_mask())) { + MFParamsBuilder params(fn, 1); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState &variable_state = *param_variable_states[param_index]; + variable_states.add_as_param__one(variable_state, params, param_type, mask); + } + + fn.call(IndexRange(1), params, context); + } + else { + MFParamsBuilder params(fn, mask.min_array_size()); + + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + VariableState &variable_state = *param_variable_states[param_index]; + variable_states.add_as_param(variable_state, params, param_type, mask); + } + + fn.call(mask, params, context); + } +} + +/** An index mask, that might own the indices if necessary. */ +struct InstructionIndices { + bool is_owned; + Vector owned_indices; + IndexMask referenced_indices; + + IndexMask mask() const + { + if (this->is_owned) { + return this->owned_indices.as_span(); + } + return this->referenced_indices; + } +}; + +/** Contains information about the next instruction that should be executed. */ +struct NextInstructionInfo { + const MFInstruction *instruction = nullptr; + InstructionIndices indices; + + IndexMask mask() const + { + return this->indices.mask(); + } + + operator bool() const + { + return this->instruction != nullptr; + } +}; + +/** + * Keeps track of the next instruction for all indices and decides in which order instructions are + * evaluated. + */ +class InstructionScheduler { + private: + Map> indices_by_instruction_; + + public: + InstructionScheduler() = default; + + void add_referenced_indices(const MFInstruction &instruction, IndexMask mask) + { + if (mask.is_empty()) { + return; + } + InstructionIndices new_indices; + new_indices.is_owned = false; + new_indices.referenced_indices = mask; + indices_by_instruction_.lookup_or_add_default(&instruction).append(std::move(new_indices)); + } + + void add_owned_indices(const MFInstruction &instruction, Vector indices) + { + if (indices.is_empty()) { + return; + } + BLI_assert(IndexMask::indices_are_valid_index_mask(indices)); + + InstructionIndices new_indices; + new_indices.is_owned = true; + new_indices.owned_indices = std::move(indices); + indices_by_instruction_.lookup_or_add_default(&instruction).append(std::move(new_indices)); + } + + void add_previous_instruction_indices(const MFInstruction &instruction, + NextInstructionInfo &instr_info) + { + indices_by_instruction_.lookup_or_add_default(&instruction) + .append(std::move(instr_info.indices)); + } + + NextInstructionInfo pop_next() + { + if (indices_by_instruction_.is_empty()) { + return {}; + } + /* TODO: Implement better mechanism to determine next instruction. */ + const MFInstruction *instruction = *indices_by_instruction_.keys().begin(); + + NextInstructionInfo next_instruction_info; + next_instruction_info.instruction = instruction; + next_instruction_info.indices = this->pop_indices_array(instruction); + return next_instruction_info; + } + + private: + InstructionIndices pop_indices_array(const MFInstruction *instruction) + { + Vector *indices = indices_by_instruction_.lookup_ptr(instruction); + if (indices == nullptr) { + return {}; + } + InstructionIndices r_indices = (*indices).pop_last(); + BLI_assert(!r_indices.mask().is_empty()); + if (indices->is_empty()) { + indices_by_instruction_.remove_contained(instruction); + } + return r_indices; + } +}; + +void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext context) const +{ + BLI_assert(procedure_.validate()); + + LinearAllocator<> allocator; + + VariableStates variable_states{full_mask}; + variable_states.add_initial_variable_states(*this, procedure_, params); + + InstructionScheduler scheduler; + scheduler.add_referenced_indices(*procedure_.entry(), full_mask); + + /* Loop until all indices got to a return instruction. */ + while (NextInstructionInfo instr_info = scheduler.pop_next()) { + const MFInstruction &instruction = *instr_info.instruction; + switch (instruction.type()) { + case MFInstructionType::Call: { + const MFCallInstruction &call_instruction = static_cast( + instruction); + execute_call_instruction(call_instruction, instr_info.mask(), variable_states, context); + scheduler.add_previous_instruction_indices(*call_instruction.next(), instr_info); + break; + } + case MFInstructionType::Branch: { + const MFBranchInstruction &branch_instruction = static_cast( + instruction); + const MFVariable *condition_var = branch_instruction.condition(); + VariableState &variable_state = variable_states.get_variable_state(*condition_var); + + IndicesSplitVectors new_indices; + variable_state.indices_split(instr_info.mask(), new_indices); + scheduler.add_owned_indices(*branch_instruction.branch_false(), new_indices[false]); + scheduler.add_owned_indices(*branch_instruction.branch_true(), new_indices[true]); + break; + } + case MFInstructionType::Destruct: { + const MFDestructInstruction &destruct_instruction = + static_cast(instruction); + const MFVariable *variable = destruct_instruction.variable(); + variable_states.destruct(*variable, instr_info.mask()); + scheduler.add_previous_instruction_indices(*destruct_instruction.next(), instr_info); + break; + } + case MFInstructionType::Dummy: { + const MFDummyInstruction &dummy_instruction = static_cast( + instruction); + scheduler.add_previous_instruction_indices(*dummy_instruction.next(), instr_info); + break; + } + case MFInstructionType::Return: { + /* Don't insert the indices back into the scheduler. */ + break; + } + } + } + + for (const int param_index : this->param_indices()) { + const MFParamType param_type = this->param_type(param_index); + const MFVariable *variable = procedure_.params()[param_index].variable; + VariableState &variable_state = variable_states.get_variable_state(*variable); + switch (param_type.interface_type()) { + case MFParamType::Input: { + /* Input variables must be destructed in the end. */ + BLI_assert(variable_state.is_fully_uninitialized(full_mask)); + break; + } + case MFParamType::Mutable: + case MFParamType::Output: { + /* Mutable and output variables must be initialized in the end. */ + BLI_assert(variable_state.is_fully_initialized(full_mask)); + /* Make sure that the data is in the memory provided by the caller. */ + variable_state.ensure_is_mutable( + full_mask, param_type.data_type(), variable_states.value_allocator()); + break; + } + } + } +} + +} // namespace blender::fn diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc new file mode 100644 index 00000000000..212b79e75d3 --- /dev/null +++ b/source/blender/functions/tests/FN_field_test.cc @@ -0,0 +1,278 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_cpp_type.hh" +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +namespace blender::fn::tests { + +TEST(field, ConstantFunction) +{ + /* TODO: Figure out how to not use another "FieldOperation(" inside of std::make_shared. */ + GField constant_field{std::make_shared( + FieldOperation(std::make_unique>(10), {})), + 0}; + + Array result(4); + + FieldContext context; + FieldEvaluator evaluator{context, 4}; + evaluator.add_with_destination(constant_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[0], 10); + EXPECT_EQ(result[1], 10); + EXPECT_EQ(result[2], 10); + EXPECT_EQ(result[3], 10); +} + +class IndexFieldInput final : public FieldInput { + public: + IndexFieldInput() : FieldInput(CPPType::get(), "Index") + { + } + + const GVArray *get_varray_for_context(const FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const final + { + auto index_func = [](int i) { return i; }; + return &scope.construct< + GVArray_For_EmbeddedVArray>>( + __func__, mask.min_array_size(), mask.min_array_size(), index_func); + } +}; + +TEST(field, VArrayInput) +{ + GField index_field{std::make_shared()}; + + Array result_1(4); + + FieldContext context; + FieldEvaluator evaluator{context, 4}; + evaluator.add_with_destination(index_field, result_1.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[0], 0); + EXPECT_EQ(result_1[1], 1); + EXPECT_EQ(result_1[2], 2); + EXPECT_EQ(result_1[3], 3); + + /* Evaluate a second time, just to test that the first didn't break anything. */ + Array result_2(10); + + const Array indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldEvaluator evaluator_2{context, &mask}; + evaluator_2.add_with_destination(index_field, result_2.as_mutable_span()); + evaluator_2.evaluate(); + EXPECT_EQ(result_2[2], 2); + EXPECT_EQ(result_2[4], 4); + EXPECT_EQ(result_2[6], 6); + EXPECT_EQ(result_2[8], 8); +} + +TEST(field, VArrayInputMultipleOutputs) +{ + std::shared_ptr index_input = std::make_shared(); + GField field_1{index_input}; + GField field_2{index_input}; + + Array result_1(10); + Array result_2(10); + + const Array indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(field_1, result_1.as_mutable_span()); + evaluator.add_with_destination(field_2, result_2.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[2], 2); + EXPECT_EQ(result_1[4], 4); + EXPECT_EQ(result_1[6], 6); + EXPECT_EQ(result_1[8], 8); + EXPECT_EQ(result_2[2], 2); + EXPECT_EQ(result_2[4], 4); + EXPECT_EQ(result_2[6], 6); + EXPECT_EQ(result_2[8], 8); +} + +TEST(field, InputAndFunction) +{ + GField index_field{std::make_shared()}; + + std::unique_ptr add_fn = std::make_unique>( + "add", [](int a, int b) { return a + b; }); + GField output_field{std::make_shared( + FieldOperation(std::move(add_fn), {index_field, index_field})), + 0}; + + Array result(10); + + const Array indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(output_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[2], 4); + EXPECT_EQ(result[4], 8); + EXPECT_EQ(result[6], 12); + EXPECT_EQ(result[8], 16); +} + +TEST(field, TwoFunctions) +{ + GField index_field{std::make_shared()}; + + std::unique_ptr add_fn = std::make_unique>( + "add", [](int a, int b) { return a + b; }); + GField add_field{std::make_shared( + FieldOperation(std::move(add_fn), {index_field, index_field})), + 0}; + + std::unique_ptr add_10_fn = std::make_unique>( + "add_10", [](int a) { return a + 10; }); + GField result_field{ + std::make_shared(FieldOperation(std::move(add_10_fn), {add_field})), 0}; + + Array result(10); + + const Array indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(result_field, result.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result[2], 14); + EXPECT_EQ(result[4], 18); + EXPECT_EQ(result[6], 22); + EXPECT_EQ(result[8], 26); +} + +class TwoOutputFunction : public MultiFunction { + private: + MFSignature signature_; + + public: + TwoOutputFunction(StringRef name) + { + MFSignatureBuilder signature{name}; + signature.single_input("In1"); + signature.single_input("In2"); + signature.single_output("Add"); + signature.single_output("Add10"); + signature_ = signature.build(); + this->set_signature(&signature_); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + const VArray &in1 = params.readonly_single_input(0, "In1"); + const VArray &in2 = params.readonly_single_input(1, "In2"); + MutableSpan add = params.uninitialized_single_output(2, "Add"); + MutableSpan add_10 = params.uninitialized_single_output(3, "Add10"); + mask.foreach_index([&](const int64_t i) { + add[i] = in1[i] + in2[i]; + add_10[i] = add[i] + 10; + }); + } +}; + +TEST(field, FunctionTwoOutputs) +{ + /* Also use two separate input fields, why not. */ + GField index_field_1{std::make_shared()}; + GField index_field_2{std::make_shared()}; + + std::shared_ptr fn = std::make_shared(FieldOperation( + std::make_unique("SI_SI_SO_SO"), {index_field_1, index_field_2})); + + GField result_field_1{fn, 0}; + GField result_field_2{fn, 1}; + + Array result_1(10); + Array result_2(10); + + const Array indices = {2, 4, 6, 8}; + const IndexMask mask{indices}; + + FieldContext context; + FieldEvaluator evaluator{context, &mask}; + evaluator.add_with_destination(result_field_1, result_1.as_mutable_span()); + evaluator.add_with_destination(result_field_2, result_2.as_mutable_span()); + evaluator.evaluate(); + EXPECT_EQ(result_1[2], 4); + EXPECT_EQ(result_1[4], 8); + EXPECT_EQ(result_1[6], 12); + EXPECT_EQ(result_1[8], 16); + EXPECT_EQ(result_2[2], 14); + EXPECT_EQ(result_2[4], 18); + EXPECT_EQ(result_2[6], 22); + EXPECT_EQ(result_2[8], 26); +} + +TEST(field, TwoFunctionsTwoOutputs) +{ + GField index_field{std::make_shared()}; + + std::shared_ptr fn = std::make_shared(FieldOperation( + std::make_unique("SI_SI_SO_SO"), {index_field, index_field})); + + Array mask_indices = {2, 4, 6, 8}; + IndexMask mask = mask_indices.as_span(); + + Field result_field_1{fn, 0}; + Field intermediate_field{fn, 1}; + + std::unique_ptr add_10_fn = std::make_unique>( + "add_10", [](int a) { return a + 10; }); + Field result_field_2{ + std::make_shared(FieldOperation(std::move(add_10_fn), {intermediate_field})), + 0}; + + FieldContext field_context; + FieldEvaluator field_evaluator{field_context, &mask}; + const VArray *result_1 = nullptr; + const VArray *result_2 = nullptr; + field_evaluator.add(result_field_1, &result_1); + field_evaluator.add(result_field_2, &result_2); + field_evaluator.evaluate(); + + EXPECT_EQ(result_1->get(2), 4); + EXPECT_EQ(result_1->get(4), 8); + EXPECT_EQ(result_1->get(6), 12); + EXPECT_EQ(result_1->get(8), 16); + EXPECT_EQ(result_2->get(2), 24); + EXPECT_EQ(result_2->get(4), 28); + EXPECT_EQ(result_2->get(6), 32); + EXPECT_EQ(result_2->get(8), 36); +} + +TEST(field, SameFieldTwice) +{ + GField constant_field{ + std::make_shared(std::make_unique>(10)), 0}; + + FieldContext field_context; + IndexMask mask{IndexRange(2)}; + ResourceScope scope; + Vector results = evaluate_fields( + scope, {constant_field, constant_field}, mask, field_context); + + GVArray_Typed varray1{*results[0]}; + GVArray_Typed varray2{*results[1]}; + + EXPECT_EQ(varray1->get(0), 10); + EXPECT_EQ(varray1->get(1), 10); + EXPECT_EQ(varray2->get(0), 10); + EXPECT_EQ(varray2->get(1), 10); +} + +} // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_procedure_test.cc b/source/blender/functions/tests/FN_multi_function_procedure_test.cc new file mode 100644 index 00000000000..0b4b88fba3d --- /dev/null +++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc @@ -0,0 +1,344 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure_builder.hh" +#include "FN_multi_function_procedure_executor.hh" +#include "FN_multi_function_test_common.hh" + +namespace blender::fn::tests { + +TEST(multi_function_procedure, SimpleTest) +{ + /** + * procedure(int var1, int var2, int *var4) { + * int var3 = var1 + var2; + * var4 = var2 + var3; + * var4 += 10; + * } + */ + + CustomMF_SI_SI_SO add_fn{"add", [](int a, int b) { return a + b; }}; + CustomMF_SM add_10_fn{"add_10", [](int &a) { a += 10; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter(); + MFVariable *var2 = &builder.add_single_input_parameter(); + auto [var3] = builder.add_call<1>(add_fn, {var1, var2}); + auto [var4] = builder.add_call<1>(add_fn, {var2, var3}); + builder.add_call(add_10_fn, {var4}); + builder.add_destruct({var1, var2, var3}); + builder.add_return(); + builder.add_output_parameter(*var4); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor executor{"My Procedure", procedure}; + + MFParamsBuilder params{executor, 3}; + MFContextBuilder context; + + Array input_array = {1, 2, 3}; + params.add_readonly_single_input(input_array.as_span()); + params.add_readonly_single_input_value(3); + + Array output_array(3); + params.add_uninitialized_single_output(output_array.as_mutable_span()); + + executor.call(IndexRange(3), params, context); + + EXPECT_EQ(output_array[0], 17); + EXPECT_EQ(output_array[1], 18); + EXPECT_EQ(output_array[2], 19); +} + +TEST(multi_function_procedure, BranchTest) +{ + /** + * procedure(int &var1, bool var2) { + * if (var2) { + * var1 += 100; + * } + * else { + * var1 += 10; + * } + * var1 += 10; + * } + */ + + CustomMF_SM add_10_fn{"add_10", [](int &a) { a += 10; }}; + CustomMF_SM add_100_fn{"add_100", [](int &a) { a += 100; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_mutable_parameter(); + MFVariable *var2 = &builder.add_single_input_parameter(); + + MFProcedureBuilder::Branch branch = builder.add_branch(*var2); + branch.branch_false.add_call(add_10_fn, {var1}); + branch.branch_true.add_call(add_100_fn, {var1}); + builder.set_cursor_after_branch(branch); + builder.add_call(add_10_fn, {var1}); + builder.add_destruct({var2}); + builder.add_return(); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Condition Test", procedure}; + MFParamsBuilder params(procedure_fn, 5); + + Array values_a = {1, 5, 3, 6, 2}; + Array values_cond = {true, false, true, true, false}; + + params.add_single_mutable(values_a.as_mutable_span()); + params.add_readonly_single_input(values_cond.as_span()); + + MFContextBuilder context; + procedure_fn.call({1, 2, 3, 4}, params, context); + + EXPECT_EQ(values_a[0], 1); + EXPECT_EQ(values_a[1], 25); + EXPECT_EQ(values_a[2], 113); + EXPECT_EQ(values_a[3], 116); + EXPECT_EQ(values_a[4], 22); +} + +TEST(multi_function_procedure, EvaluateOne) +{ + /** + * procedure(int var1, int *var2) { + * var2 = var1 + 10; + * } + */ + + int tot_evaluations = 0; + CustomMF_SI_SO add_10_fn{"add_10", [&](int a) { + tot_evaluations++; + return a + 10; + }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var1 = &builder.add_single_input_parameter(); + auto [var2] = builder.add_call<1>(add_10_fn, {var1}); + builder.add_destruct(*var1); + builder.add_return(); + builder.add_output_parameter(*var2); + + MFProcedureExecutor procedure_fn{"Evaluate One", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array values_out = {1, 2, 3, 4, 5}; + params.add_readonly_single_input_value(1); + params.add_uninitialized_single_output(values_out.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(values_out[0], 11); + EXPECT_EQ(values_out[1], 11); + EXPECT_EQ(values_out[2], 3); + EXPECT_EQ(values_out[3], 11); + EXPECT_EQ(values_out[4], 11); + /* We expect only one evaluation, because the input is constant. */ + EXPECT_EQ(tot_evaluations, 1); +} + +TEST(multi_function_procedure, SimpleLoop) +{ + /** + * procedure(int count, int *out) { + * out = 1; + * int index = 0' + * loop { + * if (index >= count) { + * break; + * } + * out *= 2; + * index += 1; + * } + * out += 1000; + * } + */ + + CustomMF_Constant const_1_fn{1}; + CustomMF_Constant const_0_fn{0}; + CustomMF_SI_SI_SO greater_or_equal_fn{"greater or equal", + [](int a, int b) { return a >= b; }}; + CustomMF_SM double_fn{"double", [](int &a) { a *= 2; }}; + CustomMF_SM add_1000_fn{"add 1000", [](int &a) { a += 1000; }}; + CustomMF_SM add_1_fn{"add 1", [](int &a) { a += 1; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_count = &builder.add_single_input_parameter("count"); + auto [var_out] = builder.add_call<1>(const_1_fn); + var_out->set_name("out"); + auto [var_index] = builder.add_call<1>(const_0_fn); + var_index->set_name("index"); + + MFProcedureBuilder::Loop loop = builder.add_loop(); + auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count}); + var_condition->set_name("condition"); + MFProcedureBuilder::Branch branch = builder.add_branch(*var_condition); + branch.branch_true.add_destruct(*var_condition); + branch.branch_true.add_loop_break(loop); + branch.branch_false.add_destruct(*var_condition); + builder.set_cursor_after_branch(branch); + builder.add_call(double_fn, {var_out}); + builder.add_call(add_1_fn, {var_index}); + builder.add_loop_continue(loop); + builder.set_cursor_after_loop(loop); + builder.add_call(add_1000_fn, {var_out}); + builder.add_destruct({var_count, var_index}); + builder.add_return(); + builder.add_output_parameter(*var_out); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Simple Loop", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array counts = {4, 3, 7, 6, 4}; + Array results(5, -1); + + params.add_readonly_single_input(counts.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(results[0], 1016); + EXPECT_EQ(results[1], 1008); + EXPECT_EQ(results[2], -1); + EXPECT_EQ(results[3], 1064); + EXPECT_EQ(results[4], 1016); +} + +TEST(multi_function_procedure, Vectors) +{ + /** + * procedure(vector v1, vector &v2, vector *v3) { + * v1.extend(v2); + * int constant = 5; + * v2.append(constant); + * v2.extend(v1); + * int len = sum(v2); + * v3 = range(len); + * } + */ + + CreateRangeFunction create_range_fn; + ConcatVectorsFunction extend_fn; + GenericAppendFunction append_fn{CPPType::get()}; + SumVectorFunction sum_elements_fn; + CustomMF_Constant constant_5_fn{5}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_v1 = &builder.add_input_parameter(MFDataType::ForVector()); + MFVariable *var_v2 = &builder.add_parameter(MFParamType::ForMutableVector(CPPType::get())); + builder.add_call(extend_fn, {var_v1, var_v2}); + auto [var_constant] = builder.add_call<1>(constant_5_fn); + builder.add_call(append_fn, {var_v2, var_constant}); + builder.add_destruct(*var_constant); + builder.add_call(extend_fn, {var_v2, var_v1}); + auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2}); + auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len}); + builder.add_destruct({var_v1, var_len}); + builder.add_return(); + builder.add_output_parameter(*var_v3); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Vectors", procedure}; + MFParamsBuilder params{procedure_fn, 5}; + + Array v1 = {5, 2, 3}; + GVectorArray v2{CPPType::get(), 5}; + GVectorArray v3{CPPType::get(), 5}; + + int value_10 = 10; + v2.append(0, &value_10); + v2.append(4, &value_10); + + params.add_readonly_vector_input(v1.as_span()); + params.add_vector_mutable(v2); + params.add_vector_output(v3); + + MFContextBuilder context; + procedure_fn.call({0, 1, 3, 4}, params, context); + + EXPECT_EQ(v2[0].size(), 6); + EXPECT_EQ(v2[1].size(), 4); + EXPECT_EQ(v2[2].size(), 0); + EXPECT_EQ(v2[3].size(), 4); + EXPECT_EQ(v2[4].size(), 6); + + EXPECT_EQ(v3[0].size(), 35); + EXPECT_EQ(v3[1].size(), 15); + EXPECT_EQ(v3[2].size(), 0); + EXPECT_EQ(v3[3].size(), 15); + EXPECT_EQ(v3[4].size(), 35); +} + +TEST(multi_function_procedure, BufferReuse) +{ + /** + * procedure(int a, int *out) { + * int b = a + 10; + * int c = c + 10; + * int d = d + 10; + * int e = d + 10; + * out = e + 10; + * } + */ + + CustomMF_SI_SO add_10_fn{"add 10", [](int a) { return a + 10; }}; + + MFProcedure procedure; + MFProcedureBuilder builder{procedure}; + + MFVariable *var_a = &builder.add_single_input_parameter(); + auto [var_b] = builder.add_call<1>(add_10_fn, {var_a}); + builder.add_destruct(*var_a); + auto [var_c] = builder.add_call<1>(add_10_fn, {var_b}); + builder.add_destruct(*var_b); + auto [var_d] = builder.add_call<1>(add_10_fn, {var_c}); + builder.add_destruct(*var_c); + auto [var_e] = builder.add_call<1>(add_10_fn, {var_d}); + builder.add_destruct(*var_d); + auto [var_out] = builder.add_call<1>(add_10_fn, {var_e}); + builder.add_destruct(*var_e); + builder.add_return(); + builder.add_output_parameter(*var_out); + + EXPECT_TRUE(procedure.validate()); + + MFProcedureExecutor procedure_fn{"Buffer Reuse", procedure}; + + Array inputs = {4, 1, 6, 2, 3}; + Array results(5, -1); + + MFParamsBuilder params{procedure_fn, 5}; + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(results.as_mutable_span()); + + MFContextBuilder context; + procedure_fn.call({0, 2, 3, 4}, params, context); + + EXPECT_EQ(results[0], 54); + EXPECT_EQ(results[1], -1); + EXPECT_EQ(results[2], 56); + EXPECT_EQ(results[3], 52); + EXPECT_EQ(results[4], 53); +} + +} // namespace blender::fn::tests diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index e5282409329..6acea8da15f 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -31,6 +31,8 @@ extern "C" { #endif +struct AnonymousAttributeID; + /** Descriptor and storage for a custom data layer. */ typedef struct CustomDataLayer { /** Type of data in layer. */ @@ -53,6 +55,13 @@ typedef struct CustomDataLayer { char name[64]; /** Layer data. */ void *data; + /** + * Run-time identifier for this layer. If no one has a strong reference to this id anymore, + * the layer can be removed. The custom data layer only has a weak reference to the id, because + * otherwise there will always be a strong reference and the attribute can't be removed + * automatically. + */ + const struct AnonymousAttributeID *anonymous_id; } CustomDataLayer; #define MAX_CUSTOMDATA_LAYER_NAME 64 diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index fd67150811d..82eabf6995c 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1441,6 +1441,13 @@ typedef struct NodeGeometryCurveFill { uint8_t mode; } NodeGeometryCurveFill; +typedef struct NodeGeometryAttributeCapture { + /* CustomDataType. */ + int8_t data_type; + /* AttributeDomain. */ + int8_t domain; +} NodeGeometryAttributeCapture; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 1cb561ddd9f..7160d2c3751 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -646,7 +646,8 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char _pad[5]; + char use_geometry_nodes_fields; + char _pad[4]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index a7c0372f309..b294e93310c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10284,6 +10284,26 @@ static void def_geo_curve_fill(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_attribute_capture(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryAttributeCapture", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to store the data in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index fe1b0757690..0a328c3b068 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6300,6 +6300,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1); RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); + + prop = RNA_def_property(srna, "use_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1); + RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 47fb6826ca3..ba557c3976c 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -85,6 +85,7 @@ #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field.hh" #include "FN_multi_function.hh" using blender::destruct_ptr; @@ -410,28 +411,36 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, { switch (socket_value_type) { case SOCK_FLOAT: { + float value = 0.0f; if (property.type == IDP_FLOAT) { - *(float *)r_value = IDP_Float(&property); + value = IDP_Float(&property); } else if (property.type == IDP_DOUBLE) { - *(float *)r_value = (float)IDP_Double(&property); + value = (float)IDP_Double(&property); } + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); break; } case SOCK_INT: { - *(int *)r_value = IDP_Int(&property); + int value = IDP_Int(&property); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); break; } case SOCK_VECTOR: { - copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); + float3 value; + copy_v3_v3(value, (const float *)IDP_Array(&property)); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); break; } case SOCK_BOOLEAN: { - *(bool *)r_value = IDP_Int(&property) != 0; + bool value = IDP_Int(&property) != 0; + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); break; } case SOCK_STRING: { - new (r_value) std::string(IDP_String(&property)); + std::string value = IDP_String(&property); + new (r_value) + blender::fn::Field(blender::fn::make_constant_field(std::move(value))); break; } case SOCK_OBJECT: { diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 5646e37707c..4f21924619b 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -21,6 +21,8 @@ #include "DEG_depsgraph_query.h" +#include "FN_field.hh" +#include "FN_field_cpp_type.hh" #include "FN_generic_value_map.hh" #include "FN_multi_function.hh" @@ -33,6 +35,9 @@ namespace blender::modifiers::geometry_nodes { using fn::CPPType; +using fn::Field; +using fn::FieldCPPType; +using fn::GField; using fn::GValueMap; using nodes::GeoNodeExecParams; using namespace fn::multi_function_types; @@ -858,11 +863,10 @@ class GeometryNodesEvaluator { const MultiFunction &fn, NodeState &node_state) { - MFContextBuilder fn_context; - MFParamsBuilder fn_params{fn, 1}; LinearAllocator<> &allocator = local_allocators_.local(); /* Prepare the inputs for the multi function. */ + Vector input_fields; for (const int i : node->inputs().index_range()) { const InputSocketRef &socket_ref = node->input(i); if (!socket_ref.is_available()) { @@ -873,24 +877,12 @@ class GeometryNodesEvaluator { BLI_assert(input_state.was_ready_for_execution); SingleInputValue &single_value = *input_state.value.single; BLI_assert(single_value.value != nullptr); - fn_params.add_readonly_single_input(GPointer{*input_state.type, single_value.value}); - } - /* Prepare the outputs for the multi function. */ - Vector outputs; - for (const int i : node->outputs().index_range()) { - const OutputSocketRef &socket_ref = node->output(i); - if (!socket_ref.is_available()) { - continue; - } - const CPPType &type = *get_socket_cpp_type(socket_ref); - void *buffer = allocator.allocate(type.size(), type.alignment()); - fn_params.add_uninitialized_single_output(GMutableSpan{type, buffer, 1}); - outputs.append({type, buffer}); + input_fields.append(std::move(*(GField *)single_value.value)); } - fn.call(IndexRange(1), fn_params, fn_context); + auto operation = std::make_shared(fn, std::move(input_fields)); - /* Forward the computed outputs. */ + /* Forward outputs. */ int output_index = 0; for (const int i : node->outputs().index_range()) { const OutputSocketRef &socket_ref = node->output(i); @@ -899,8 +891,9 @@ class GeometryNodesEvaluator { } OutputState &output_state = node_state.outputs[i]; const DOutputSocket socket{node.context(), &socket_ref}; - GMutablePointer value = outputs[output_index]; - this->forward_output(socket, value); + const CPPType *cpp_type = get_socket_cpp_type(socket_ref); + GField &field = *allocator.construct(operation, output_index).release(); + this->forward_output(socket, {cpp_type, &field}); output_state.has_been_computed = true; output_index++; } @@ -922,7 +915,7 @@ class GeometryNodesEvaluator { OutputState &output_state = node_state.outputs[socket->index()]; output_state.has_been_computed = true; void *buffer = allocator.allocate(type->size(), type->alignment()); - type->copy_construct(type->default_value(), buffer); + this->construct_default_value(*type, buffer); this->forward_output({node.context(), socket}, {*type, buffer}); } } @@ -1389,14 +1382,42 @@ class GeometryNodesEvaluator { return; } + const FieldCPPType *from_field_type = dynamic_cast(&from_type); + const FieldCPPType *to_field_type = dynamic_cast(&to_type); + + if (from_field_type != nullptr && to_field_type != nullptr) { + const CPPType &from_base_type = from_field_type->field_type(); + const CPPType &to_base_type = to_field_type->field_type(); + if (conversions_.is_convertible(from_base_type, to_base_type)) { + const MultiFunction &fn = *conversions_.get_conversion_multi_function( + MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + const GField &from_field = *(const GField *)from_value; + auto operation = std::make_shared(fn, Vector{from_field}); + new (to_value) GField(std::move(operation), 0); + return; + } + } if (conversions_.is_convertible(from_type, to_type)) { /* Do the conversion if possible. */ conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value); } else { /* Cannot convert, use default value instead. */ - to_type.copy_construct(to_type.default_value(), to_value); + this->construct_default_value(to_type, to_value); + } + } + + void construct_default_value(const CPPType &type, void *r_value) + { + if (const FieldCPPType *field_cpp_type = dynamic_cast(&type)) { + const CPPType &base_type = field_cpp_type->field_type(); + auto constant_fn = std::make_unique(base_type, + base_type.default_value()); + auto operation = std::make_shared(std::move(constant_fn)); + new (r_value) GField(std::move(operation), 0); + return; } + type.copy_construct(type.default_value(), r_value); } NodeState &get_node_state(const DNode node) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 1e7ac4b92f6..ac2200d496b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -142,6 +142,7 @@ set(SRC function/node_function_util.cc geometry/nodes/node_geo_align_rotation_to_vector.cc + geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_clamp.cc geometry/nodes/node_geo_attribute_color_ramp.cc geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -187,6 +188,9 @@ set(SRC geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc + geometry/nodes/node_geo_input_normal.cc + geometry/nodes/node_geo_input_position.cc + geometry/nodes/node_geo_input_index.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_assign.cc @@ -212,6 +216,7 @@ set(SRC geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc + geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc geometry/nodes/node_geo_transform.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 00062400eee..e9fb4ad123c 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -37,6 +37,7 @@ void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_fill(void); +void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); @@ -71,7 +72,10 @@ void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_input_index(void); void register_node_type_geo_input_material(void); +void register_node_type_geo_input_normal(void); +void register_node_type_geo_input_position(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); @@ -99,6 +103,7 @@ void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_set_position(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index d6a23051c0b..e0972e40a64 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -16,7 +16,8 @@ #pragma once -#include "FN_generic_value_map.hh" +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" #include "BKE_attribute_access.hh" #include "BKE_geometry_set.hh" @@ -32,17 +33,24 @@ struct ModifierData; namespace blender::nodes { +using bke::AttributeIDRef; using bke::geometry_set_realize_instances; +using bke::GeometryComponentFieldContext; using bke::OutputAttribute; using bke::OutputAttribute_Typed; using bke::ReadAttributeLookup; +using bke::StrongAnonymousAttributeID; +using bke::WeakAnonymousAttributeID; using bke::WriteAttributeLookup; using fn::CPPType; +using fn::Field; +using fn::FieldInput; +using fn::FieldOperation; +using fn::GField; using fn::GMutablePointer; using fn::GMutableSpan; using fn::GPointer; using fn::GSpan; -using fn::GValueMap; using fn::GVArray; using fn::GVArray_GSpan; using fn::GVArray_Span; @@ -121,6 +129,14 @@ class GeoNodeExecParams { { } + template + static inline constexpr bool is_stored_as_field_v = std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; + /** * Get the input value for the input socket with the given identifier. * @@ -142,11 +158,17 @@ class GeoNodeExecParams { */ template T extract_input(StringRef identifier) { + if constexpr (is_stored_as_field_v) { + Field field = this->extract_input>(identifier); + return fn::evaluate_constant_field(field); + } + else { #ifdef DEBUG - this->check_input_access(identifier, &CPPType::get()); + this->check_input_access(identifier, &CPPType::get()); #endif - GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out(); + GMutablePointer gvalue = this->extract_input(identifier); + return gvalue.relocate_out(); + } } /** @@ -159,7 +181,13 @@ class GeoNodeExecParams { Vector gvalues = provider_->extract_multi_input(identifier); Vector values; for (GMutablePointer gvalue : gvalues) { - values.append(gvalue.relocate_out()); + if constexpr (is_stored_as_field_v) { + const Field &field = *gvalue.get>(); + values.append(fn::evaluate_constant_field(field)); + } + else { + values.append(gvalue.relocate_out()); + } } return values; } @@ -167,14 +195,20 @@ class GeoNodeExecParams { /** * Get the input value for the input socket with the given identifier. */ - template const T &get_input(StringRef identifier) const + template const T get_input(StringRef identifier) const { + if constexpr (is_stored_as_field_v) { + const Field &field = this->get_input>(identifier); + return fn::evaluate_constant_field(field); + } + else { #ifdef DEBUG - this->check_input_access(identifier, &CPPType::get()); + this->check_input_access(identifier, &CPPType::get()); #endif - GPointer gvalue = provider_->get_input(identifier); - BLI_assert(gvalue.is_type()); - return *(const T *)gvalue.get(); + GPointer gvalue = provider_->get_input(identifier); + BLI_assert(gvalue.is_type()); + return *(const T *)gvalue.get(); + } } /** @@ -183,13 +217,19 @@ class GeoNodeExecParams { template void set_output(StringRef identifier, T &&value) { using StoredT = std::decay_t; - const CPPType &type = CPPType::get>(); + if constexpr (is_stored_as_field_v) { + this->set_output>(identifier, + fn::make_constant_field(std::forward(value))); + } + else { + const CPPType &type = CPPType::get(); #ifdef DEBUG - this->check_output_access(identifier, type); + this->check_output_access(identifier, type); #endif - GMutablePointer gvalue = provider_->alloc_output_value(type); - new (gvalue.get()) StoredT(std::forward(value)); - provider_->set_output(identifier, gvalue); + GMutablePointer gvalue = provider_->alloc_output_value(type); + new (gvalue.get()) StoredT(std::forward(value)); + provider_->set_output(identifier, gvalue); + } } /** diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 2e9f186de02..670cde4470e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -276,6 +276,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") @@ -304,8 +305,8 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRA DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") @@ -313,7 +314,10 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_ DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") +DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") +DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") +DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") @@ -339,6 +343,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") +DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 6a69c3d24ec..015ac0de002 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -84,7 +84,7 @@ struct CurveToPointsResults { MutableSpan radii; MutableSpan tilts; - Map point_attributes; + Map point_attributes; MutableSpan tangents; MutableSpan normals; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc new file mode 100644 index 00000000000..1fa71d3f57d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -0,0 +1,210 @@ +/* + * 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. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BKE_attribute_math.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Value"); + b.add_input("Value", "Value_001"); + b.add_input("Value", "Value_002"); + b.add_input("Value", "Value_003"); + b.add_input("Value", "Value_004"); + + b.add_output("Geometry"); + b.add_output("Attribute"); + b.add_output("Attribute", "Attribute_001"); + b.add_output("Attribute", "Attribute_002"); + b.add_output("Attribute", "Attribute_003"); + b.add_output("Attribute", "Attribute_004"); +} + +static void geo_node_attribute_capture_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void geo_node_attribute_capture_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryAttributeCapture *data = (NodeGeometryAttributeCapture *)MEM_callocN( + sizeof(NodeGeometryAttributeCapture), __func__); + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_POINT; + + node->storage = data; +} + +static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) + node->storage; + const CustomDataType data_type = static_cast(storage.data_type); + + bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_value_vector = socket_value_attribute_name->next; + bNodeSocket *socket_value_float = socket_value_vector->next; + bNodeSocket *socket_value_color4f = socket_value_float->next; + bNodeSocket *socket_value_boolean = socket_value_color4f->next; + bNodeSocket *socket_value_int32 = socket_value_boolean->next; + + nodeSetSocketAvailability(socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next; + bNodeSocket *out_socket_value_float = out_socket_value_vector->next; + bNodeSocket *out_socket_value_color4f = out_socket_value_float->next; + bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next; + bNodeSocket *out_socket_value_int32 = out_socket_value_boolean->next; + + nodeSetSocketAvailability(out_socket_value_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_value_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_value_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_value_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_value_int32, data_type == CD_PROP_INT32); +} + +static void try_capture_field_on_geometry(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeDomain domain, + const GField &field) +{ + GeometryComponentFieldContext field_context{component, domain}; + const int domain_size = component.attribute_domain_size(domain); + const IndexMask mask{IndexMask(domain_size)}; + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(field.cpp_type()); + OutputAttribute output_attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + + fn::FieldEvaluator evaluator{field_context, &mask}; + evaluator.add_with_destination(field, output_attribute.varray()); + evaluator.evaluate(); + + output_attribute.save(); +} + +static void geo_node_attribute_capture_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + const bNode &node = params.node(); + const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) + node.storage; + const CustomDataType data_type = static_cast(storage.data_type); + const AttributeDomain domain = static_cast(storage.domain); + + GField field; + switch (data_type) { + case CD_PROP_FLOAT: + field = params.get_input>("Value_001"); + break; + case CD_PROP_FLOAT3: + field = params.get_input>("Value"); + break; + case CD_PROP_COLOR: + field = params.get_input>("Value_002"); + break; + case CD_PROP_BOOL: + field = params.get_input>("Value_003"); + break; + case CD_PROP_INT32: + field = params.get_input>("Value_004"); + break; + default: + break; + } + + WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + const CPPType &type = field.cpp_type(); + + static const Array types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + } + } + + GField output_field{ + std::make_shared(std::move(anonymous_id), type)}; + + switch (data_type) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field(output_field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field(output_field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field(output_field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field(output_field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field(output_field)); + break; + } + default: + break; + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_capture() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0); + node_type_storage(&ntype, + "NodeGeometryAttributeCapture", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_attribute_capture_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_capture_update); + ntype.declare = blender::nodes::geo_node_attribute_capture_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_capture_exec; + ntype.draw_buttons = blender::nodes::geo_node_attribute_capture_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc index 699fcc5e983..7853c5aa04a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc @@ -56,25 +56,26 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, Span offsets, PointCloudComponent &points) { - curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( - name, ATTR_DOMAIN_CURVE, meta_data.data_type); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - /* Only copy the attributes of splines in the offsets. */ - for (const int i : offsets.index_range()) { - spline_attribute->get(offsets[i], result[i]); - } - - result_attribute.save(); - return true; - }); + curve_component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + /* Only copy the attributes of splines in the offsets. */ + for (const int i : offsets.index_range()) { + spline_attribute->get(offsets[i], result[i]); + } + + result_attribute.save(); + return true; + }); } /** @@ -124,20 +125,20 @@ static void copy_endpoint_attributes(Span splines, /* Copy the point attribute data over. */ for (const auto &item : start_data.point_attributes.items()) { - const StringRef name = item.key; + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); } for (const auto &item : end_data.point_attributes.items()) { - const StringRef name = item.key; + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); blender::fn::GVArray_For_GSpan(spline_span).get(spline.size() - 1, point_span[i]); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 1382efa025b..6f40687540c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -84,14 +84,14 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) input_spline.radii().first()); output_spline->attributes.reallocate(1); input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional src = input_spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = input_spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!output_spline->attributes.create(name, meta_data.data_type)) { + if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } - std::optional dst = output_spline->attributes.get_for_write(name); + std::optional dst = output_spline->attributes.get_for_write(attribute_id); if (!dst) { BLI_assert_unreachable(); return false; @@ -122,15 +122,15 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) output_spline->attributes.reallocate(count); input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional input_attribute = input_spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional input_attribute = input_spline.attributes.get_for_read(attribute_id); BLI_assert(input_attribute); - if (!output_spline->attributes.create(name, meta_data.data_type)) { + if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } std::optional output_attribute = output_spline->attributes.get_for_write( - name); + attribute_id); if (!output_attribute) { BLI_assert_unreachable(); return false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index b826e1c6510..720af50b795 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -83,9 +83,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) reverse_data(splines[i]->tilts()); splines[i]->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { std::optional output_attribute = - splines[i]->attributes.get_for_write(name); + splines[i]->attributes.get_for_write(attribute_id); if (!output_attribute) { BLI_assert_unreachable(); return false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 6a093f442cb..6380873fbcb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -74,14 +74,14 @@ template static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) { input_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional src = input_spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = input_spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!output_spline.attributes.create(name, meta_data.data_type)) { + if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } - std::optional dst = output_spline.attributes.get_for_write(name); + std::optional dst = output_spline.attributes.get_for_write(attribute_id); if (!dst) { BLI_assert_unreachable(); return false; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 958d83e2967..2809543a73d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -283,16 +283,16 @@ static void subdivide_dynamic_attributes(const Spline &src_spline, { const bool is_cyclic = src_spline.is_cyclic(); src_spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional src = src_spline.attributes.get_for_read(name); + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = src_spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!dst_spline.attributes.create(name, meta_data.data_type)) { + if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { /* Since the source spline of the same type had the attribute, adding it should work. */ BLI_assert_unreachable(); } - std::optional dst = dst_spline.attributes.get_for_write(name); + std::optional dst = dst_spline.attributes.get_for_write(attribute_id); BLI_assert(dst); attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 17cd8e987a7..74740ba244f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -115,21 +115,21 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const StringRef name, + const AttributeIDRef &attribute_id, const CustomDataType data_type) { - points.attribute_try_create(name, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); - WriteAttributeLookup attribute = points.attribute_try_get_for_write(name); + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); BLI_assert(attribute); return attribute.varray->get_internal_span(); } template static MutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const StringRef name) + const AttributeIDRef &attribute_id) { GMutableSpan attribute = create_attribute_and_retrieve_span( - points, name, bke::cpp_type_to_custom_data_type(CPPType::get())); + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get())); return attribute.typed(); } @@ -147,9 +147,10 @@ CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponen /* Because of the invariants of the curve component, we use the attributes of the * first spline as a representative for the attribute meta data all splines. */ curve.splines().first()->attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { attributes.point_attributes.add_new( - name, create_attribute_and_retrieve_span(points, name, meta_data.data_type)); + attribute_id, + create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); return true; }, ATTR_DOMAIN_POINT); @@ -179,12 +180,12 @@ static void copy_evaluated_point_attributes(Span splines, spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); - for (const Map::Item &item : data.point_attributes.items()) { - const StringRef name = item.key; + for (const Map::Item &item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.interpolate_to_evaluated(spline_span) ->materialize(point_span.slice(offset, size).data()); @@ -222,12 +223,12 @@ static void copy_uniform_sample_point_attributes(Span splines, uniform_samples, data.tilts.slice(offset, size)); - for (const Map::Item &item : data.point_attributes.items()) { - const StringRef name = item.key; + for (const Map::Item &item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; - BLI_assert(spline.attributes.get_for_read(name)); - GSpan spline_span = *spline.attributes.get_for_read(name); + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), uniform_samples, @@ -257,31 +258,32 @@ static void copy_spline_domain_attributes(const CurveComponent &curve_component, Span offsets, PointCloudComponent &points) { - curve_component.attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( - name, ATTR_DOMAIN_CURVE, meta_data.data_type); - const CPPType &type = spline_attribute->type(); - - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - for (const int i : IndexRange(spline_attribute->size())) { - const int offset = offsets[i]; - const int size = offsets[i + 1] - offsets[i]; - if (size != 0) { - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - spline_attribute->get(i, buffer); - type.fill_assign_n(buffer, result[offset], size); - } - } - - result_attribute.save(); - return true; - }); + curve_component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); + const CPPType &type = spline_attribute->type(); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + for (const int i : IndexRange(spline_attribute->size())) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size != 0) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + spline_attribute->get(i, buffer); + type.fill_assign_n(buffer, result[offset], size); + } + } + + result_attribute.save(); + return true; + }); } void curve_create_default_rotation_attribute(Span tangents, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 578f0298a19..4ad311d63c5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -158,8 +158,8 @@ static void trim_poly_spline(Spline &spline, linear_trim_data(start, end, spline.tilts()); spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - std::optional src = spline.attributes.get_for_write(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional src = spline.attributes.get_for_write(attribute_id); BLI_assert(src); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { using T = decltype(dummy); @@ -193,14 +193,14 @@ static PolySpline trim_nurbs_spline(const Spline &spline, /* Copy generic attribute data. */ spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional src = spline.attributes.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = spline.attributes.get_for_read(attribute_id); BLI_assert(src); - if (!new_spline.attributes.create(name, meta_data.data_type)) { + if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) { BLI_assert_unreachable(); return false; } - std::optional dst = new_spline.attributes.get_for_write(name); + std::optional dst = new_spline.attributes.get_for_write(attribute_id); BLI_assert(dst); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { @@ -249,8 +249,8 @@ static void trim_bezier_spline(Spline &spline, linear_trim_data(start, end, bezier_spline.radii()); linear_trim_data(start, end, bezier_spline.tilts()); spline.attributes.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { - std::optional src = spline.attributes.get_for_write(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional src = spline.attributes.get_for_write(attribute_id); BLI_assert(src); attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 6bc0ab49959..b36b12d8cf5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -93,17 +93,17 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src, const IndexMask mask) { src.foreach_attribute( - [&](StringRefNull name, const AttributeMetaData &meta_data) { - std::optional src_attribute = src.get_for_read(name); + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.get_for_read(attribute_id); BLI_assert(src_attribute); - if (!dst.create(name, meta_data.data_type)) { + if (!dst.create(attribute_id, meta_data.data_type)) { /* Since the source spline of the same type had the attribute, adding it should work. */ BLI_assert_unreachable(); } - std::optional new_attribute = dst.get_for_write(name); + std::optional new_attribute = dst.get_for_write(attribute_id); BLI_assert(new_attribute); attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc new file mode 100644 index 00000000000..e2287abe56c --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_index_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Index"); +} + +class IndexFieldInput final : public fn::FieldInput { + public: + IndexFieldInput() : FieldInput(CPPType::get(), "Index") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const final + { + /* TODO: Investigate a similar method to IndexRange::as_span() */ + auto index_func = [](int i) { return i; }; + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + __func__, mask.min_array_size(), mask.min_array_size(), index_func); + } +}; + +static void geo_node_input_index_exec(GeoNodeExecParams params) +{ + Field index_field{std::make_shared()}; + params.set_output("Index", std::move(index_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_index() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_INDEX, "Index", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_index_exec; + ntype.declare = blender::nodes::geo_node_input_index_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc new file mode 100644 index 00000000000..b8f126ef1db --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -0,0 +1,211 @@ +/* + * 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. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Normal"); +} + +static GVArrayPtr mesh_face_normals(const Mesh &mesh, + const Span verts, + const Span polys, + const Span loops, + const IndexMask mask) +{ + /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ + if (!(mesh.runtime.cd_dirty_poly & CD_MASK_NORMAL) && + CustomData_has_layer(&mesh.pdata, CD_NORMAL)) { + const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); + + return std::make_unique>( + Span((const float3 *)data, polys.size())); + } + + auto normal_fn = [verts, polys, loops](const int i) -> float3 { + float3 normal; + const MPoly &poly = polys[i]; + BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), normal); + return normal; + }; + + return std::make_unique< + fn::GVArray_For_EmbeddedVArray>>( + mask.min_array_size(), mask.min_array_size(), normal_fn); +} + +static GVArrayPtr mesh_vertex_normals(const Mesh &mesh, + const Span verts, + const Span polys, + const Span loops, + const IndexMask mask) +{ + /* Use existing normals to avoid unnecessarily recalculating them, if possible. */ + if (!(mesh.runtime.cd_dirty_vert & CD_MASK_NORMAL) && + CustomData_has_layer(&mesh.vdata, CD_NORMAL)) { + const void *data = CustomData_get_layer(&mesh.pdata, CD_NORMAL); + + return std::make_unique>( + Span((const float3 *)data, mesh.totvert)); + } + + /* If the normals are dirty, they must be recalculated for the output of this node's field + * source. Ideally vertex normals could be calculated lazily on a const mesh, but that's not + * possible at the moment, so we take ownership of the results. Sadly we must also create a copy + * of MVert to use the mesh normals API. This can be improved by adding mutex-protected lazy + * calculation of normals on meshes. + * + * Use mask.min_array_size() to avoid calculating a final chunk of data if possible. */ + Array temp_verts(verts); + Array normals(verts.size()); /* Use full size for accumulation from faces. */ + BKE_mesh_calc_normals_poly_and_vertex(temp_verts.data(), + mask.min_array_size(), + loops.data(), + loops.size(), + polys.data(), + polys.size(), + nullptr, + (float(*)[3])normals.data()); + + return std::make_unique>>(std::move(normals)); +} + +static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_component, + const Mesh &mesh, + const IndexMask mask, + const AttributeDomain domain, + ResourceScope &scope) +{ + Span verts{mesh.mvert, mesh.totvert}; + Span edges{mesh.medge, mesh.totedge}; + Span polys{mesh.mpoly, mesh.totpoly}; + Span loops{mesh.mloop, mesh.totloop}; + + switch (domain) { + case ATTR_DOMAIN_FACE: { + return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask), __func__).get(); + } + case ATTR_DOMAIN_POINT: { + return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask), __func__).get(); + } + case ATTR_DOMAIN_EDGE: { + /* In this case, start with vertex normals and convert to the edge domain, since the + * conversion from edges to vertices is very simple. Use the full mask since the edges + * might use the vertex normal from any index. */ + GVArrayPtr vert_normals = mesh_vertex_normals( + mesh, verts, polys, loops, IndexRange(verts.size())); + Span vert_normals_span = vert_normals->get_internal_span().typed(); + Array edge_normals(mask.min_array_size()); + + /* Use "manual" domain interpolation instead of the GeometryComponent API to avoid + * calculating unnecessary values and to allow normalizing the result much more simply. */ + for (const int i : mask) { + const MEdge &edge = edges[i]; + edge_normals[i] = float3::interpolate( + vert_normals_span[edge.v1], vert_normals_span[edge.v2], 0.5f) + .normalized(); + } + + return &scope.construct>>( + __func__, std::move(edge_normals)); + } + case ATTR_DOMAIN_CORNER: { + /* The normals on corners are just the mesh's face normals, so start with the face normal + * array and copy the face normal for each of its corners. */ + GVArrayPtr face_normals = mesh_face_normals( + mesh, verts, polys, loops, IndexRange(polys.size())); + + /* In this case using the mesh component's generic domain interpolation is fine, the data + * will still be normalized, since the face normal is just copied to every corner. */ + GVArrayPtr loop_normals = mesh_component.attribute_try_adapt_domain( + std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); + return scope.add_value(std::move(loop_normals), __func__).get(); + } + default: + return nullptr; + } +} + +class NormalFieldInput final : public fn::FieldInput { + public: + NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal") + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent &mesh_component = static_cast(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return nullptr; + } + + return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope); + } + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + /* TODO: Add curve normals support. */ + return nullptr; + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 669605641; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_input_normal_exec(GeoNodeExecParams params) +{ + Field normal_field{std::make_shared()}; + params.set_output("Normal", std::move(normal_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_normal() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_NORMAL, "Normal", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_normal_exec; + ntype.declare = blender::nodes::geo_node_input_normal_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc new file mode 100644 index 00000000000..c6365bf6809 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_position_declare(NodeDeclarationBuilder &b) +{ + b.add_output("Position"); +} + +static void geo_node_input_position_exec(GeoNodeExecParams params) +{ + Field position_field{ + std::make_shared("position", CPPType::get())}; + params.set_output("Position", std::move(position_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_position() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_POSITION, "Position", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_position_exec; + ntype.declare = blender::nodes::geo_node_input_position_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 730cf08feaa..5792ee1485a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -161,34 +161,35 @@ static Array to_base_components(Span get_final_attribute_info( +static Map get_final_attribute_info( Span components, Span ignored_attributes) { - Map info; + Map info; for (const GeometryComponent *component : components) { - component->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) { - if (ignored_attributes.contains(name)) { - return true; - } - info.add_or_modify( - name, - [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, - [&](AttributeMetaData *meta_data_final) { - meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( - {meta_data_final->data_type, meta_data.data_type}); - meta_data_final->domain = blender::bke::attribute_domain_highest_priority( - {meta_data_final->domain, meta_data.domain}); - }); - return true; - }); + component->attribute_foreach( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named() && ignored_attributes.contains(attribute_id.name())) { + return true; + } + info.add_or_modify( + attribute_id, + [&](AttributeMetaData *meta_data_final) { *meta_data_final = meta_data; }, + [&](AttributeMetaData *meta_data_final) { + meta_data_final->data_type = blender::bke::attribute_data_type_highest_complexity( + {meta_data_final->data_type, meta_data.data_type}); + meta_data_final->domain = blender::bke::attribute_domain_highest_priority( + {meta_data_final->domain, meta_data.domain}); + }); + return true; + }); } return info; } static void fill_new_attribute(Span src_components, - StringRef attribute_name, + const AttributeIDRef &attribute_id, const CustomDataType data_type, const AttributeDomain domain, GMutableSpan dst_span) @@ -203,7 +204,7 @@ static void fill_new_attribute(Span src_components, continue; } GVArrayPtr read_attribute = component->attribute_get_for_read( - attribute_name, domain, data_type, nullptr); + attribute_id, domain, data_type, nullptr); GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); @@ -218,20 +219,21 @@ static void join_attributes(Span src_components, GeometryComponent &result, Span ignored_attributes = {}) { - const Map info = get_final_attribute_info(src_components, - ignored_attributes); + const Map info = get_final_attribute_info(src_components, + ignored_attributes); - for (const Map::Item &item : info.items()) { - const StringRef name = item.key; + for (const Map::Item &item : info.items()) { + const AttributeIDRef attribute_id = item.key; const AttributeMetaData &meta_data = item.value; OutputAttribute write_attribute = result.attribute_try_get_for_output_only( - name, meta_data.domain, meta_data.data_type); + attribute_id, meta_data.domain, meta_data.data_type); if (!write_attribute) { continue; } GMutableSpan dst_span = write_attribute.as_span(); - fill_new_attribute(src_components, name, meta_data.data_type, meta_data.domain, dst_span); + fill_new_attribute( + src_components, attribute_id, meta_data.data_type, meta_data.domain, dst_span); write_attribute.save(); } } @@ -306,7 +308,7 @@ static void join_components(Span src_components, Geomet * \note This takes advantage of the fact that creating attributes on joined curves never * changes a point attribute into a spline attribute; it is always the other way around. */ -static void ensure_control_point_attribute(const StringRef name, +static void ensure_control_point_attribute(const AttributeIDRef &attribute_id, const CustomDataType data_type, Span src_components, CurveEval &result) @@ -321,7 +323,7 @@ static void ensure_control_point_attribute(const StringRef name, const CurveEval *current_curve = src_components[src_component_index]->get_for_read(); for (SplinePtr &spline : splines) { - std::optional attribute = spline->attributes.get_for_read(name); + std::optional attribute = spline->attributes.get_for_read(attribute_id); if (attribute) { if (attribute->type() != type) { @@ -334,22 +336,22 @@ static void ensure_control_point_attribute(const StringRef name, conversions.try_convert(std::make_unique(*attribute), type) ->materialize(converted_buffer); - spline->attributes.remove(name); - spline->attributes.create_by_move(name, data_type, converted_buffer); + spline->attributes.remove(attribute_id); + spline->attributes.create_by_move(attribute_id, data_type, converted_buffer); } } else { - spline->attributes.create(name, data_type); + spline->attributes.create(attribute_id, data_type); - if (current_curve->attributes.get_for_read(name)) { + if (current_curve->attributes.get_for_read(attribute_id)) { /* In this case the attribute did not exist, but there is a spline domain attribute * we can retrieve a value from, as a spline to point domain conversion. So fill the * new attribute with the value for this spline. */ GVArrayPtr current_curve_attribute = current_curve->attributes.get_for_read( - name, data_type, nullptr); + attribute_id, data_type, nullptr); - BLI_assert(spline->attributes.get_for_read(name)); - std::optional new_attribute = spline->attributes.get_for_write(name); + BLI_assert(spline->attributes.get_for_read(attribute_id)); + std::optional new_attribute = spline->attributes.get_for_write(attribute_id); BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); current_curve_attribute->get(spline_index_in_component, buffer); @@ -371,15 +373,15 @@ static void ensure_control_point_attribute(const StringRef name, /** * Fill data for an attribute on the new curve based on all source curves. */ -static void ensure_spline_attribute(const StringRef name, +static void ensure_spline_attribute(const AttributeIDRef &attribute_id, const CustomDataType data_type, Span src_components, CurveEval &result) { const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); - result.attributes.create(name, data_type); - GMutableSpan result_attribute = *result.attributes.get_for_write(name); + result.attributes.create(attribute_id, data_type); + GMutableSpan result_attribute = *result.attributes.get_for_write(attribute_id); int offset = 0; for (const CurveComponent *component : src_components) { @@ -388,7 +390,7 @@ static void ensure_spline_attribute(const StringRef name, if (size == 0) { continue; } - GVArrayPtr read_attribute = curve.attributes.get_for_read(name, data_type, nullptr); + GVArrayPtr read_attribute = curve.attributes.get_for_read(attribute_id, data_type, nullptr); GVArray_GSpan src_span{*read_attribute}; const void *src_buffer = src_span.data(); @@ -406,19 +408,19 @@ static void ensure_spline_attribute(const StringRef name, * \warning Splines have been moved out of the source components at this point, so it * is important to only read curve-level data (spline domain attributes) from them. */ -static void join_curve_attributes(const Map &info, +static void join_curve_attributes(const Map &info, Span src_components, CurveEval &result) { - for (const Map::Item &item : info.items()) { - const StringRef name = item.key; + for (const Map::Item &item : info.items()) { + const AttributeIDRef attribute_id = item.key; const AttributeMetaData meta_data = item.value; if (meta_data.domain == ATTR_DOMAIN_CURVE) { - ensure_spline_attribute(name, meta_data.data_type, src_components, result); + ensure_spline_attribute(attribute_id, meta_data.data_type, src_components, result); } else { - ensure_control_point_attribute(name, meta_data.data_type, src_components, result); + ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result); } } } @@ -446,7 +448,7 @@ static void join_curve_components(MutableSpan src_geometry_sets, Ge } /* Retrieve attribute info before moving the splines out of the input components. */ - const Map info = get_final_attribute_info( + const Map info = get_final_attribute_info( {(const GeometryComponent **)src_components.data(), src_components.size()}, {"position", "radius", "tilt", "cyclic", "resolution"}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 2cea60ea112..298c6fc1ffe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -52,10 +52,10 @@ static void copy_attributes_to_points(CurveEval &curve, Span> point_to_vert_maps) { MutableSpan splines = curve.splines(); - Set source_attribute_names = mesh_component.attribute_names(); + Set source_attribute_ids = mesh_component.attribute_ids(); /* Copy builtin control point attributes. */ - if (source_attribute_names.contains_as("tilt")) { + if (source_attribute_ids.contains("tilt")) { const GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( "tilt", ATTR_DOMAIN_POINT, 0.0f); threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { @@ -64,9 +64,9 @@ static void copy_attributes_to_points(CurveEval &curve, *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); } }); - source_attribute_names.remove_contained_as("tilt"); + source_attribute_ids.remove_contained("tilt"); } - if (source_attribute_names.contains_as("radius")) { + if (source_attribute_ids.contains("radius")) { const GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( "radius", ATTR_DOMAIN_POINT, 1.0f); threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { @@ -75,15 +75,15 @@ static void copy_attributes_to_points(CurveEval &curve, *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); } }); - source_attribute_names.remove_contained_as("radius"); + source_attribute_ids.remove_contained("radius"); } /* Don't copy other builtin control point attributes. */ - source_attribute_names.remove_as("position"); + source_attribute_ids.remove("position"); /* Copy dynamic control point attributes. */ - for (const StringRef name : source_attribute_names) { - const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(name, + for (const AttributeIDRef &attribute_id : source_attribute_ids) { + const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, ATTR_DOMAIN_POINT); /* Some attributes might not exist if they were builtin attribute on domains that don't * have any elements, i.e. a face attribute on the output of the line primitive node. */ @@ -96,8 +96,9 @@ static void copy_attributes_to_points(CurveEval &curve, threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { /* Create attribute on the spline points. */ - splines[i]->attributes.create(name, data_type); - std::optional spline_attribute = splines[i]->attributes.get_for_write(name); + splines[i]->attributes.create(attribute_id, data_type); + std::optional spline_attribute = splines[i]->attributes.get_for_write( + attribute_id); BLI_assert(spline_attribute); /* Copy attribute based on the map for this spline. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index cf874bea718..dcab1ea3fb2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -277,17 +277,17 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, BLI_NOINLINE static void interpolate_existing_attributes( Span set_groups, Span instance_start_offsets, - const Map &attributes, + const Map &attributes, GeometryComponent &component, Span> bary_coords_array, Span> looptri_indices_array) { - for (Map::Item entry : attributes.items()) { - StringRef attribute_name = entry.key; + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; const CustomDataType output_data_type = entry.value.data_type; /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ OutputAttribute attribute_out = component.attribute_try_get_for_output_only( - attribute_name, ATTR_DOMAIN_POINT, output_data_type); + attribute_id, ATTR_DOMAIN_POINT, output_data_type); if (!attribute_out) { continue; } @@ -301,7 +301,7 @@ BLI_NOINLINE static void interpolate_existing_attributes( const Mesh &mesh = *source_component.get_for_read(); std::optional attribute_info = component.attribute_get_meta_data( - attribute_name); + attribute_id); if (!attribute_info) { i_instance += set_group.transforms.size(); continue; @@ -309,7 +309,7 @@ BLI_NOINLINE static void interpolate_existing_attributes( const AttributeDomain source_domain = attribute_info->domain; GVArrayPtr source_attribute = source_component.attribute_get_for_read( - attribute_name, source_domain, output_data_type, nullptr); + attribute_id, source_domain, output_data_type, nullptr); if (!source_attribute) { i_instance += set_group.transforms.size(); continue; @@ -406,7 +406,7 @@ BLI_NOINLINE static void compute_special_attributes(Span BLI_NOINLINE static void add_remaining_point_attributes( Span set_groups, Span instance_start_offsets, - const Map &attributes, + const Map &attributes, GeometryComponent &component, Span> bary_coords_array, Span> looptri_indices_array) @@ -629,7 +629,7 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) PointCloudComponent &point_component = geometry_set_out.get_component_for_write(); - Map attributes; + Map attributes; bke::geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_MESH}, {"position", "normal", "id"}, attributes); add_remaining_point_attributes(set_groups, diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 1b0061346c4..8752c1c669e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -53,8 +53,8 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, Span masks, const bool invert) { - for (const std::string &name : in_component.attribute_names()) { - ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(name); + for (const AttributeIDRef &attribute_id : in_component.attribute_ids()) { + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); /* Only copy point attributes. Theoretically this could interpolate attributes on other @@ -65,7 +65,7 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, } OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( - name, ATTR_DOMAIN_POINT, data_type); + attribute_id, ATTR_DOMAIN_POINT, data_type); attribute_math::convert_to_static_type(data_type, [&](auto dummy) { using T = decltype(dummy); diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc new file mode 100644 index 00000000000..e8591616f55 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#include "DEG_depsgraph_query.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_position_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Position"); + b.add_input("Selection").default_value(true); + b.add_output("Geometry"); +} + +static void set_position_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &position_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed positions = component.attribute_try_get_for_output( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + fn::FieldEvaluator position_evaluator{field_context, &selection}; + position_evaluator.add_with_destination(position_field, positions.varray()); + position_evaluator.evaluate(); + positions.save(); +} + +static void geo_node_set_position_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input("Geometry"); + geometry = geometry_set_realize_instances(geometry); + Field selection_field = params.extract_input>("Selection"); + Field position_field = params.extract_input>("Position"); + + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (geometry.has(type)) { + set_position_in_component( + geometry.get_component_for_write(type), selection_field, position_field); + } + } + + params.set_output("Geometry", std::move(geometry)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_position() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_POSITION, "Set Position", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_position_exec; + ntype.declare = blender::nodes::geo_node_set_position_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 7487f11d77d..3b3b643d0ae 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -161,8 +161,10 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful { bke::geometry_set_instances_attribute_foreach( geometry_set, - [&](StringRefNull attribute_name, const AttributeMetaData &meta_data) { - this->attributes_.append({attribute_name, meta_data.domain, meta_data.data_type}); + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (attribute_id.is_named()) { + this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); + } return true; }, 8); diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index d386781e3ce..8efd6c55459 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -48,6 +48,7 @@ #include "NOD_socket.h" #include "FN_cpp_type_make.hh" +#include "FN_field.hh" using namespace blender; using blender::nodes::SocketDeclarationPtr; @@ -701,8 +702,14 @@ static bNodeSocketType *make_socket_type_bool() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + bool value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -713,8 +720,14 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + float value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -725,8 +738,14 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + int value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -737,8 +756,14 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + blender::float3 value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -751,8 +776,15 @@ static bNodeSocketType *make_socket_type_rgba() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + blender::ColorGeometry4f value; + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) + blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } @@ -763,8 +795,15 @@ static bNodeSocketType *make_socket_type_string() socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); }; - socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; - socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + socktype->get_geometry_nodes_cpp_type = []() { + return &blender::fn::CPPType::get>(); + }; + socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + std::string value; + value.~basic_string(); + socket.typeinfo->get_base_cpp_value(socket, &value); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + }; return socktype; } -- cgit v1.2.3 From da50cd86a745c9e63fc645bad6f0a5412afff920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 9 Sep 2021 11:33:16 +0200 Subject: Cleanup: clarify comment about the use of `_PyThreadState_UncheckedGet()` No functional changes. --- source/blender/python/generic/bpy_threads.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/python/generic/bpy_threads.c b/source/blender/python/generic/bpy_threads.c index bd707f728a1..57eb3a82c44 100644 --- a/source/blender/python/generic/bpy_threads.c +++ b/source/blender/python/generic/bpy_threads.c @@ -29,7 +29,9 @@ /* analogue of PyEval_SaveThread() */ BPy_ThreadStatePtr BPY_thread_save(void) { - /* The thread-state can be NULL when quitting Blender. */ + /* Use `_PyThreadState_UncheckedGet()`, instead of more canonical `PyGILState_Check()` or + * `PyThreadState_Get()`, to avoid a fatal error issued when a thread state is NULL (the thread + * state can be NULL when quitting Blender). */ if (_PyThreadState_UncheckedGet()) { return (BPy_ThreadStatePtr)PyEval_SaveThread(); } -- cgit v1.2.3 From fc460351170478e712740ae1917a2e24803eba3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 9 Sep 2021 14:27:00 +0200 Subject: Depsgraph: release GIL when evaluating the depsgraph Evaluating the dependency graph potentially executes Python code when evaluating drivers. In specific situations (see T91046) this could deadlock Blender entirely. Temporarily releasing the GIL when evaluating the depsgraph resolves this. Calling the `BPy_BEGIN_ALLOW_THREADS` macro is relatively safe, as it's a no-op when the current thread does not have the GIL. Developed in collaboration with @sergey Manifest task: T91046 --- source/blender/depsgraph/CMakeLists.txt | 7 +++++++ source/blender/depsgraph/intern/eval/deg_eval.cc | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 3ad26c6f4db..41253117096 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -161,6 +161,13 @@ set(LIB bf_blenkernel ) +if(WITH_PYTHON) + add_definitions(-DWITH_PYTHON) + list(APPEND INC + ../python + ) +endif() + blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index ad88cf656ad..c816c7b8db5 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -41,6 +41,10 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + #include "atomic_ops.h" #include "intern/depsgraph.h" @@ -375,6 +379,11 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->debug.begin_graph_evaluation(); +#ifdef WITH_PYTHON + /* Release the GIL so that Python drivers can be evaluated. See T91046. */ + BPy_BEGIN_ALLOW_THREADS; +#endif + graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ @@ -415,6 +424,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph) deg_graph_clear_tags(graph); graph->is_evaluating = false; +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif + graph->debug.end_graph_evaluation(); } -- cgit v1.2.3 From 07c6af413617a93ea8c8c3b1318844b3c5ce9aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Kivist=C3=B6?= Date: Thu, 9 Sep 2021 22:37:36 +0900 Subject: XR: Support for Varjo OpenXR extensions This adds support for two Varjo specific OpenXR vendor extensions: 1) XR_VARJO_QUAD_VIEWS 2) XR_VARJO_FOVEATED_RENDERING Together these enable human eye resolution rendering on supported devices (currently mainly Varjo XR-3 and VR-3). In addition, there's a detection for Varjo OpenXR runtime. This has been tested on real Varjo XR-3 hardware and Varjo Simulator and confirmed to function correctly. Foveation works, and the views are rendered correctly for all the four views. Reviewed By: Peter Kim, Julian Eisel Differential Revision: https://developer.blender.org/D12229 --- intern/ghost/intern/GHOST_XrContext.cpp | 16 ++++++- intern/ghost/intern/GHOST_XrContext.h | 3 ++ intern/ghost/intern/GHOST_XrSession.cpp | 82 +++++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index a7498e9f91f..88da39914e7 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -135,7 +135,8 @@ void GHOST_XrContext::storeInstanceProperties() {"Monado(XRT) by Collabora et al", OPENXR_RUNTIME_MONADO}, {"Oculus", OPENXR_RUNTIME_OCULUS}, {"SteamVR/OpenXR", OPENXR_RUNTIME_STEAMVR}, - {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}}; + {"Windows Mixed Reality Runtime", OPENXR_RUNTIME_WMR}, + {"Varjo OpenXR Runtime", OPENXR_RUNTIME_VARJO}}; decltype(runtime_map)::const_iterator runtime_map_iter; m_oxr->instance_properties.type = XR_TYPE_INSTANCE_PROPERTIES; @@ -415,6 +416,12 @@ void GHOST_XrContext::getExtensionsToEnable( try_ext.push_back(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME); try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME); + /* Varjo quad view extension. */ + try_ext.push_back(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME); + + /* Varjo foveated extension. */ + try_ext.push_back(XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME); + r_ext_names.reserve(try_ext.size() + graphics_binding_types.size()); /* Add graphics binding extensions (may be multiple ones, we'll settle for one to use later, once @@ -613,4 +620,11 @@ bool GHOST_XrContext::isDebugTimeMode() const return m_debug_time; } +bool GHOST_XrContext::isExtensionEnabled(const char *ext) const +{ + bool contains = std::find(m_enabled_extensions.begin(), m_enabled_extensions.end(), ext) != + m_enabled_extensions.end(); + return contains; +} + /** \} */ /* Ghost Internal Accessors and Mutators */ diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index f29d7349f7e..26fc374b957 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -51,6 +51,7 @@ enum GHOST_TXrOpenXRRuntimeID { OPENXR_RUNTIME_OCULUS, OPENXR_RUNTIME_STEAMVR, OPENXR_RUNTIME_WMR, /* Windows Mixed Reality */ + OPENXR_RUNTIME_VARJO, OPENXR_RUNTIME_UNKNOWN }; @@ -94,6 +95,8 @@ class GHOST_XrContext : public GHOST_IXrContext { bool isDebugMode() const; bool isDebugTimeMode() const; + bool isExtensionEnabled(const char *ext) const; + private: static GHOST_XrErrorHandlerFn s_error_handler; static void *s_error_handler_customdata; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index a08b2d6045a..cd930c8328b 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -41,10 +41,13 @@ struct OpenXRSessionData { XrSession session = XR_NULL_HANDLE; XrSessionState session_state = XR_SESSION_STATE_UNKNOWN; - /* Only stereo rendering supported now. */ - const XrViewConfigurationType view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + /* Use stereo rendering by default. */ + XrViewConfigurationType view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + bool foveation_supported = false; + XrSpace reference_space; XrSpace view_space; + XrSpace combined_eye_space; std::vector views; std::vector swapchains; @@ -58,6 +61,9 @@ struct GHOST_XrDrawInfo { std::chrono::high_resolution_clock::time_point frame_begin_time; /* Time previous frames took for rendering (in ms). */ std::list last_frame_times; + + /* Whether foveation is active for the frame. */ + bool foveation_active; }; /* -------------------------------------------------------------------- */ @@ -82,6 +88,9 @@ GHOST_XrSession::~GHOST_XrSession() if (m_oxr->view_space != XR_NULL_HANDLE) { CHECK_XR_ASSERT(xrDestroySpace(m_oxr->view_space)); } + if (m_oxr->combined_eye_space != XR_NULL_HANDLE) { + CHECK_XR_ASSERT(xrDestroySpace(m_oxr->combined_eye_space)); + } if (m_oxr->session != XR_NULL_HANDLE) { CHECK_XR_ASSERT(xrDestroySession(m_oxr->session)); } @@ -189,6 +198,13 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.view_space), "Failed to create view reference space."); + + /* Foveation reference spaces. */ + if (oxr.foveation_supported) { + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO; + CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.combined_eye_space), + "Failed to create combined eye reference space."); + } } void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) @@ -292,9 +308,19 @@ GHOST_XrSession::LifeExpectancy GHOST_XrSession::handleStateChangeEvent( void GHOST_XrSession::prepareDrawing() { + assert(m_context->getInstance() != XR_NULL_HANDLE); + std::vector view_configs; uint32_t view_count; + /* Attempt to use quad view if supported. */ + if (m_context->isExtensionEnabled(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME)) { + m_oxr->view_type = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO; + } + + m_oxr->foveation_supported = m_context->isExtensionEnabled( + XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME); + CHECK_XR( xrEnumerateViewConfigurationViews( m_context->getInstance(), m_oxr->system_id, m_oxr->view_type, 0, &view_count, nullptr), @@ -306,7 +332,36 @@ void GHOST_XrSession::prepareDrawing() view_configs.size(), &view_count, view_configs.data()), - "Failed to get count of view configurations."); + "Failed to get view configurations."); + + /* If foveated rendering is used, query the foveated views. */ + if (m_oxr->foveation_supported) { + std::vector request_foveated_config{ + view_count, {XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO, nullptr, XR_TRUE}}; + + auto foveated_views = std::vector(view_count, + {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + + for (uint32_t i = 0; i < view_count; i++) { + foveated_views[i].next = &request_foveated_config[i]; + } + CHECK_XR(xrEnumerateViewConfigurationViews(m_context->getInstance(), + m_oxr->system_id, + m_oxr->view_type, + view_configs.size(), + &view_count, + foveated_views.data()), + "Failed to get foveated view configurations."); + + /* Ensure swapchains have correct size even when foveation is being used. */ + for (uint32_t i = 0; i < view_count; i++) { + view_configs[i].recommendedImageRectWidth = std::max( + view_configs[i].recommendedImageRectWidth, foveated_views[i].recommendedImageRectWidth); + view_configs[i].recommendedImageRectHeight = std::max( + view_configs[i].recommendedImageRectHeight, + foveated_views[i].recommendedImageRectHeight); + } + } for (const XrViewConfigurationView &view_config : view_configs) { m_oxr->swapchains.emplace_back(*m_gpu_binding, m_oxr->session, view_config); @@ -327,6 +382,20 @@ void GHOST_XrSession::beginFrameDrawing() CHECK_XR(xrWaitFrame(m_oxr->session, &wait_info, &frame_state), "Failed to synchronize frame rates between Blender and the device."); + /* Check if we have foveation available for the current frame. */ + m_draw_info->foveation_active = false; + if (m_oxr->foveation_supported) { + XrSpaceLocation render_gaze_location{XR_TYPE_SPACE_LOCATION}; + CHECK_XR(xrLocateSpace(m_oxr->combined_eye_space, + m_oxr->view_space, + frame_state.predictedDisplayTime, + &render_gaze_location), + "Failed to locate combined eye space."); + + m_draw_info->foveation_active = (render_gaze_location.locationFlags & + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) != 0; + } + CHECK_XR(xrBeginFrame(m_oxr->session, &begin_info), "Failed to submit frame rendering start state."); @@ -442,6 +511,8 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( std::vector &r_proj_layer_views, void *draw_customdata) { XrViewLocateInfo viewloc_info = {XR_TYPE_VIEW_LOCATE_INFO}; + XrViewLocateFoveatedRenderingVARJO foveated_info{ + XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO, nullptr, true}; XrViewState view_state = {XR_TYPE_VIEW_STATE}; XrCompositionLayerProjection layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; XrSpaceLocation view_location{XR_TYPE_SPACE_LOCATION}; @@ -451,6 +522,10 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( viewloc_info.displayTime = m_draw_info->frame_state.predictedDisplayTime; viewloc_info.space = m_oxr->reference_space; + if (m_draw_info->foveation_active) { + viewloc_info.next = &foveated_info; + } + CHECK_XR(xrLocateViews(m_oxr->session, &viewloc_info, &view_state, @@ -458,6 +533,7 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( &view_count, m_oxr->views.data()), "Failed to query frame view and projection state."); + assert(m_oxr->swapchains.size() == view_count); CHECK_XR( -- cgit v1.2.3 From 3eb6569b38d7c84ac084117221e475f5a328bf0d Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Thu, 9 Sep 2021 23:21:19 +0900 Subject: Fix build error on Linux 07c6af413617 was missing include. --- intern/ghost/intern/GHOST_XrContext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 88da39914e7..98c87232882 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -20,6 +20,7 @@ * Abstraction for XR (VR, AR, MR, ..) access via OpenXR. */ +#include #include #include #include -- cgit v1.2.3 From 62ec88eb846a8c342e046bc5c798022cbe694026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Thu, 9 Sep 2021 16:39:45 +0200 Subject: Cleanup: use NODE_SOCKET_API_ARRAY for array sockets This prevents copying the arrays when setting new values in the sockets. No functional changes. --- intern/cycles/render/hair.h | 8 ++++---- intern/cycles/render/nodes.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/intern/cycles/render/hair.h b/intern/cycles/render/hair.h index 1a8f422e8c4..e4451d70767 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/render/hair.h @@ -89,10 +89,10 @@ class Hair : public Geometry { float4 r_keys[4]) const; }; - NODE_SOCKET_API(array, curve_keys) - NODE_SOCKET_API(array, curve_radius) - NODE_SOCKET_API(array, curve_first_key) - NODE_SOCKET_API(array, curve_shader) + NODE_SOCKET_API_ARRAY(array, curve_keys) + NODE_SOCKET_API_ARRAY(array, curve_radius) + NODE_SOCKET_API_ARRAY(array, curve_first_key) + NODE_SOCKET_API_ARRAY(array, curve_shader) /* BVH */ size_t curvekey_offset; diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 99cb0b779b8..3013e9b1866 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -128,7 +128,7 @@ class ImageTextureNode : public ImageSlotTextureNode { NODE_SOCKET_API(float, projection_blend) NODE_SOCKET_API(bool, animated) NODE_SOCKET_API(float3, vector) - NODE_SOCKET_API(array, tiles) + NODE_SOCKET_API_ARRAY(array, tiles) protected: void cull_tiles(Scene *scene, ShaderGraph *graph); @@ -1554,7 +1554,7 @@ class CurvesNode : public ShaderNode { return NODE_GROUP_LEVEL_3; } - NODE_SOCKET_API(array, curves) + NODE_SOCKET_API_ARRAY(array, curves) NODE_SOCKET_API(float, min_x) NODE_SOCKET_API(float, max_x) NODE_SOCKET_API(float, fac) @@ -1588,8 +1588,8 @@ class RGBRampNode : public ShaderNode { return NODE_GROUP_LEVEL_1; } - NODE_SOCKET_API(array, ramp) - NODE_SOCKET_API(array, ramp_alpha) + NODE_SOCKET_API_ARRAY(array, ramp) + NODE_SOCKET_API_ARRAY(array, ramp_alpha) NODE_SOCKET_API(float, fac) NODE_SOCKET_API(bool, interpolate) }; -- cgit v1.2.3 From b4fd8750f96d42a53bbccf4952bab1b6f018d78f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 9 Sep 2021 09:43:00 -0500 Subject: Geometry Nodes: Allow exposing color sockets to the modifier This commit allows connecting color sockets to the group input and changing the input values in the modifier. Before there was an error since this was more complicated to support with the previous IDProperty UI data storage method. Differential Revision: https://developer.blender.org/D12437 --- source/blender/modifiers/intern/MOD_nodes.cc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index ba557c3976c..f6d145bfdd7 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -88,6 +88,7 @@ #include "FN_field.hh" #include "FN_multi_function.hh" +using blender::ColorGeometry4f; using blender::destruct_ptr; using blender::float3; using blender::FunctionRef; @@ -335,6 +336,22 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) } return property; } + case SOCK_RGBA: { + bNodeSocketValueRGBA *value = (bNodeSocketValueRGBA *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.array.len = 4; + idprop.array.type = IDP_FLOAT; + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); + copy_v4_v4((float *)IDP_Array(property), value->value); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + ui_data->base.rna_subtype = PROP_COLOR; + ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); + ui_data->default_array_len = 4; + for (const int i : IndexRange(4)) { + ui_data->default_array[i] = double(value->value[i]); + } + return property; + } case SOCK_BOOLEAN: { bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; IDPropertyTemplate idprop = {0}; @@ -391,6 +408,8 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP return property.type == IDP_INT; case SOCK_VECTOR: return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3; + case SOCK_RGBA: + return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 4; case SOCK_BOOLEAN: return property.type == IDP_INT; case SOCK_STRING: @@ -432,6 +451,12 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); break; } + case SOCK_RGBA: { + blender::ColorGeometry4f value; + copy_v4_v4((float *)value, (const float *)IDP_Array(&property)); + new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); + break; + } case SOCK_BOOLEAN: { bool value = IDP_Int(&property) != 0; new (r_value) blender::fn::Field(blender::fn::make_constant_field(value)); -- cgit v1.2.3 From ed4ef77f496e5da6bf20cfbc4165274c67b26822 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 9 Sep 2021 16:35:04 +0200 Subject: DNA: allow initializing defaults for deprecated struct members This can be useful for better forward compatibility. --- source/blender/makesdna/intern/dna_defaults.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index a573e2f9e8c..3570f5a6a6f 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -68,6 +68,8 @@ * #BLO_update_defaults_startup_blend & #blo_do_versions_userdef. */ +#define DNA_DEPRECATED_ALLOW + #include #include #include -- cgit v1.2.3 From 45c44a5b5b6251835cfd21f64c59ee23472941de Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 9 Sep 2021 17:10:09 +0200 Subject: Fix compiler warnings about virtual functions but non-virtual destructor --- source/blender/functions/FN_field.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 25188531580..79a6faf499b 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -69,7 +69,7 @@ class FieldNode { { } - ~FieldNode() = default; + virtual ~FieldNode() = default; virtual const CPPType &output_cpp_type(int output_index) const = 0; -- cgit v1.2.3 From 84d03a1298c170a3cd61c8c7f9a0177da8fcfc35 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 9 Sep 2021 09:26:44 -0700 Subject: Fix T88755: Reuse Temp Windows by Type not Title Reuse temporary windows when they share the same single area type, not because they share the same title. See D12401 for more details. Differential Revision: https://developer.blender.org/D12401 Reviewed by Campbell Barton --- source/blender/windowmanager/intern/wm_window.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 004a845c667..0402b0d778a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -808,16 +808,17 @@ wmWindow *WM_window_open(bContext *C, /* changes rect to fit within desktop */ wm_window_check_size(&rect); - /* Reuse temporary windows when they share the same title. */ + /* Reuse temporary windows when they share the same single area. */ wmWindow *win = NULL; if (temp) { LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) { - if (WM_window_is_temp_screen(win_iter)) { - char *wintitle = GHOST_GetTitle(win_iter->ghostwin); - if (STREQ(title, wintitle)) { + const bScreen *screen = WM_window_get_active_screen(win_iter); + if (screen && screen->temp && BLI_listbase_is_single(&screen->areabase)) { + ScrArea *area = screen->areabase.first; + if (space_type == (area->butspacetype ? area->butspacetype : area->spacetype)) { win = win_iter; + break; } - free(wintitle); } } } -- cgit v1.2.3 From efcf46fb6d87f1eb22ce2c0b5c9314d31269b648 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 9 Sep 2021 09:49:45 -0700 Subject: Fix T90317: Confusing File Browser Preferences Preferences / File Browser section made less confusing. See D12436 for details and comparisons. Differential Revision: https://developer.blender.org/D12436 Reviewed by Campbell Barton and Julian Eisel --- release/scripts/startup/bl_ui/space_userpref.py | 10 ++++------ source/blender/makesrna/intern/rna_userdef.c | 23 +++++++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 0d1cbc424d7..1711519bce1 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1455,13 +1455,11 @@ class USERPREF_PT_saveload_file_browser(SaveLoadPanel, CenterAlignMixIn, Panel): prefs = context.preferences paths = prefs.filepaths - col = layout.column() + col = layout.column(heading="Defaults") col.prop(paths, "use_filter_files") - - col = layout.column(heading="Hide") - col.prop(paths, "show_hidden_files_datablocks", text="Dot File & Data-Blocks") - col.prop(paths, "hide_recent_locations", text="Recent Locations") - col.prop(paths, "hide_system_bookmarks", text="System Bookmarks") + col.prop(paths, "show_hidden_files_datablocks") + col.prop(paths, "show_recent_locations") + col.prop(paths, "show_system_bookmarks") # ----------------------------------------------------------------------------- diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 0a328c3b068..b3a3d0198fe 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6073,26 +6073,25 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "File Paths", "Default paths for external files"); prop = RNA_def_property(srna, "show_hidden_files_datablocks", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_DOT); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_DOT); RNA_def_property_ui_text(prop, - "Hide Dot Files/Data-Blocks", - "Hide files and data-blocks if their name start with a dot (.*)"); + "Show Hidden Files/Data-Blocks", + "Show files and data-blocks that are normally hidden"); prop = RNA_def_property(srna, "use_filter_files", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_FILTERFILEEXTS); - RNA_def_property_ui_text(prop, - "Filter File Extensions", - "Display only files with extensions in the image select window"); + RNA_def_property_ui_text( + prop, "Filter Files", "Enable filtering of files in the File Browser"); - prop = RNA_def_property(srna, "hide_recent_locations", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_RECENT); + prop = RNA_def_property(srna, "show_recent_locations", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_RECENT); RNA_def_property_ui_text( - prop, "Hide Recent Locations", "Hide recent locations in the file selector"); + prop, "Show Recent Locations", "Show Recent locations list in the File Browser"); - prop = RNA_def_property(srna, "hide_system_bookmarks", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_HIDE_SYSTEM_BOOKMARKS); + prop = RNA_def_property(srna, "show_system_bookmarks", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_SYSTEM_BOOKMARKS); RNA_def_property_ui_text( - prop, "Hide System Bookmarks", "Hide system bookmarks in the file selector"); + prop, "Show System Locations", "Show System locations list in the File Browser"); prop = RNA_def_property(srna, "use_relative_paths", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_RELPATHS); -- cgit v1.2.3 From f13eb692692f9c622783a465e114c4e8c046782a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 9 Sep 2021 19:16:18 +0200 Subject: Revert "Depsgraph: release GIL when evaluating the depsgraph" It is causing crashes in rendering, when releasing the GIL in render threads while the main thread is holding it. Ref T91046 This reverts commit fc460351170478e712740ae1917a2e24803eba3b. --- source/blender/depsgraph/CMakeLists.txt | 7 ------- source/blender/depsgraph/intern/eval/deg_eval.cc | 13 ------------- 2 files changed, 20 deletions(-) diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 41253117096..3ad26c6f4db 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -161,13 +161,6 @@ set(LIB bf_blenkernel ) -if(WITH_PYTHON) - add_definitions(-DWITH_PYTHON) - list(APPEND INC - ../python - ) -endif() - blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index c816c7b8db5..ad88cf656ad 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -41,10 +41,6 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif - #include "atomic_ops.h" #include "intern/depsgraph.h" @@ -379,11 +375,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->debug.begin_graph_evaluation(); -#ifdef WITH_PYTHON - /* Release the GIL so that Python drivers can be evaluated. See T91046. */ - BPy_BEGIN_ALLOW_THREADS; -#endif - graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ @@ -424,10 +415,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph) deg_graph_clear_tags(graph); graph->is_evaluating = false; -#ifdef WITH_PYTHON - BPy_END_ALLOW_THREADS; -#endif - graph->debug.end_graph_evaluation(); } -- cgit v1.2.3 From c9347b9a7f579b191038ad6b457b07eb4cb2d4a9 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Thu, 9 Sep 2021 14:43:34 -0700 Subject: UI: File Browser Options Fix With D12436 two File Browser properties were renamed but two references not changed in space_filebrowser.py See D12449 for details. Differential Revision: https://developer.blender.org/D12449 Reviewed by Hans Goudey --- release/scripts/startup/bl_ui/space_filebrowser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index ca018216a5a..8ba82a7d407 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -267,7 +267,7 @@ class FILEBROWSER_PT_bookmarks_system(Panel): @classmethod def poll(cls, context): return ( - not context.preferences.filepaths.hide_system_bookmarks and + context.preferences.filepaths.show_system_bookmarks and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) ) @@ -345,7 +345,7 @@ class FILEBROWSER_PT_bookmarks_recents(Panel): @classmethod def poll(cls, context): return ( - not context.preferences.filepaths.hide_recent_locations and + context.preferences.filepaths.show_recent_locations and panel_poll_is_upper_region(context.region) and not panel_poll_is_asset_browsing(context) ) -- cgit v1.2.3 From ee3b4e8420928a03e9158489a87d5daa34ee302f Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Thu, 9 Sep 2021 17:19:58 -0600 Subject: Windows: Fix VS2022 detection VS2019 had a compiler update moving it into the range that was used to detect VS2022. This patch updates the detection to the current VS2022 preview compiler version. Reported by Jesse Y on chat. --- build_files/cmake/platform/platform_win32.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index e3183fe5b7f..cb4d196d43f 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -259,7 +259,7 @@ if(NOT DEFINED LIBDIR) else() message(FATAL_ERROR "32 bit compiler detected, blender no longer provides pre-build libraries for 32 bit windows, please set the LIBDIR cmake variable to your own library folder") endif() - if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30130) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.30.30423) message(STATUS "Visual Studio 2022 detected.") set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15) elseif(MSVC_VERSION GREATER 1919) -- cgit v1.2.3 From 82ab2c167844627ccda4ac0e439f322fcea07173 Mon Sep 17 00:00:00 2001 From: Peter Kim Date: Fri, 10 Sep 2021 13:01:14 +0900 Subject: XR: Re-enable SteamVR OpenGL backend for AMD gpus Addresses T76082. Since the DirectX backend does not work for AMD gpus (wglDXRegisterObjectNV() fails to register the shared OpenGL-DirectX render buffer, displaying a pink screen to the user), the original solution was to use SteamVR's OpenGL backend, which, as tested recently, seems to work without any issues on AMD hardware. However, the SteamVR OpenGL backend (on Windows) was disabled in fe492d922d6d since it resulted in crashes with NVIDIA gpus (and still crashes, as tested recently), so SteamVR would always use the AMD-incompatible DirectX backend (on Windows). This patch restores use of the SteamVR OpenGL backend for non-NVIDIA (AMD, etc.) gpus while maintaining the DirectX workaround for NVIDIA gpus. In this way, issues are still resolved on the NVIDIA side but AMD users can once again use the SteamVR runtime, which may be their only viable option of using Blender in VR. Reviewed By: Julian Eisel Differential Revision: https://developer.blender.org/D12409 --- intern/ghost/GHOST_Types.h | 5 +++++ intern/ghost/intern/GHOST_XrContext.cpp | 12 +++++++----- intern/ghost/intern/GHOST_XrContext.h | 3 ++- source/blender/windowmanager/xr/intern/wm_xr.c | 7 +++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index e46f712cb64..221fa140f70 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -652,6 +652,11 @@ typedef struct { enum { GHOST_kXrContextDebug = (1 << 0), GHOST_kXrContextDebugTime = (1 << 1), +# ifdef WIN32 + /* Needed to avoid issues with the SteamVR OpenGL graphics binding (use DirectX fallback + instead). */ + GHOST_kXrContextGpuNVIDIA = (1 << 2), +# endif }; typedef struct { diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 98c87232882..f6b3caa3ec7 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -99,7 +99,7 @@ void GHOST_XrContext::initialize(const GHOST_XrContextCreateInfo *create_info) storeInstanceProperties(); /* Multiple bindings may be enabled. Now that we know the runtime in use, settle for one. */ - m_gpu_binding_type = determineGraphicsBindingTypeToUse(graphics_binding_types); + m_gpu_binding_type = determineGraphicsBindingTypeToUse(graphics_binding_types, create_info); printInstanceInfo(); if (isDebugMode()) { @@ -473,14 +473,16 @@ std::vector GHOST_XrContext::determineGraphicsBindingT } GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse( - const std::vector &enabled_types) + const std::vector &enabled_types, + const GHOST_XrContextCreateInfo *create_info) { /* Return the first working type. */ for (GHOST_TXrGraphicsBinding type : enabled_types) { #ifdef WIN32 - /* The SteamVR OpenGL backend fails currently. Disable it and allow falling back to the DirectX - * one. */ - if ((m_runtime_id == OPENXR_RUNTIME_STEAMVR) && (type == GHOST_kXrGraphicsOpenGL)) { + /* The SteamVR OpenGL backend currently fails for NVIDIA gpus. Disable it and allow falling + * back to the DirectX one. */ + if ((m_runtime_id == OPENXR_RUNTIME_STEAMVR) && (type == GHOST_kXrGraphicsOpenGL) && + ((create_info->context_flag & GHOST_kXrContextGpuNVIDIA) != 0)) { continue; } #endif diff --git a/intern/ghost/intern/GHOST_XrContext.h b/intern/ghost/intern/GHOST_XrContext.h index 26fc374b957..479b50e1537 100644 --- a/intern/ghost/intern/GHOST_XrContext.h +++ b/intern/ghost/intern/GHOST_XrContext.h @@ -139,5 +139,6 @@ class GHOST_XrContext : public GHOST_IXrContext { std::vector determineGraphicsBindingTypesToEnable( const GHOST_XrContextCreateInfo *create_info); GHOST_TXrGraphicsBinding determineGraphicsBindingTypeToUse( - const std::vector &enabled_types); + const std::vector &enabled_types, + const GHOST_XrContextCreateInfo *create_info); }; diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 297205d1e79..8891840cb75 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -35,6 +35,8 @@ #include "GHOST_C-api.h" +#include "GPU_platform.h" + #include "WM_api.h" #include "wm_surface.h" @@ -91,6 +93,11 @@ bool wm_xr_init(wmWindowManager *wm) if (G.debug & G_DEBUG_XR_TIME) { create_info.context_flag |= GHOST_kXrContextDebugTime; } +#ifdef WIN32 + if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_WIN, GPU_DRIVER_ANY)) { + create_info.context_flag |= GHOST_kXrContextGpuNVIDIA; + } +#endif if (!(context = GHOST_XrContextCreate(&create_info))) { return false; -- cgit v1.2.3 From db6b3801b37adb2b0b5859dac70e8936234fca32 Mon Sep 17 00:00:00 2001 From: Jon Denning Date: Fri, 10 Sep 2021 14:34:29 +1000 Subject: Update command line argument description for --addons Changed doc string for Blender `--addons` command line argument to explain what it does rather than just describing what it expects. Reviewed By: Blendify Ref D12445 --- source/creator/creator_args.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 85ba4eca307..943646daa81 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -1920,7 +1920,7 @@ static int arg_handle_python_use_system_env_set(int UNUSED(argc), static const char arg_handle_addons_set_doc[] = "\n" - "\tComma separated list of add-ons (no spaces)."; + "\tComma separated list (no spaces) of add-ons to enable in addition to any default add-ons."; static int arg_handle_addons_set(int argc, const char **argv, void *data) { /* workaround for scripts not getting a bpy.context.scene, causes internal errors elsewhere */ -- cgit v1.2.3 From 284c9430f9bbdbe132f9eb9c4b428bf5d25df0ca Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 Sep 2021 09:21:02 +0200 Subject: Cleanup: Silenced compilation warning in ghost. --- intern/ghost/intern/GHOST_XrContext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index f6b3caa3ec7..3584f4b8e90 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -485,6 +485,8 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse( ((create_info->context_flag & GHOST_kXrContextGpuNVIDIA) != 0)) { continue; } +#else + ((void)create_info); #endif assert(type != GHOST_kXrGraphicsUnknown); -- cgit v1.2.3 From 93d2940603121acc47ea9860dac98e4e63c8f1d3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 9 Sep 2021 11:46:28 +0200 Subject: Link/Append: Fix unreported obdata being instantiated even when already used by linked/appended data. Do not instantiate obdata when it is not actually loose. --- source/blender/blenloader/intern/readfile.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9704e8bb413..15653264211 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4561,6 +4561,17 @@ static void add_loose_object_data_to_scene(Main *mainvar, active_collection = lc->collection; } + /* Do not re-instantiate obdata IDs that are already instantiated by an object. */ + LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { + if ((ob->id.tag & LIB_TAG_PRE_EXISTING) == 0 && ob->data != NULL) { + ID *obdata = ob->data; + BLI_assert(ID_REAL_USERS(obdata) > 0); + if ((obdata->tag & LIB_TAG_PRE_EXISTING) == 0) { + obdata->tag &= ~LIB_TAG_DOIT; + } + } + } + /* Loop over all ID types, instancing object-data for ID types that have support for it. */ ListBase *lbarray[INDEX_ID_MAX]; int i = set_listbasepointers(mainvar, lbarray); -- cgit v1.2.3 From fe4286435c54b3ba0a031f032b95615da74a7aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 10 Sep 2021 10:57:05 +0200 Subject: Depsgraph: release GIL when evaluating the depsgraph Evaluating the dependency graph potentially executes Python code when evaluating drivers. In specific situations (see T91046) this could deadlock Blender entirely. Temporarily releasing the GIL when evaluating the depsgraph resolves this. This is an improved version of rBfc460351170478e712740ae1917a2e24803eba3b, thanks @brecht for the diff! Manifest task: T91046 --- source/blender/depsgraph/CMakeLists.txt | 7 +++++++ source/blender/depsgraph/intern/eval/deg_eval.cc | 13 +++++++++++++ source/blender/python/generic/bpy_threads.c | 7 +++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index 3ad26c6f4db..41253117096 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -161,6 +161,13 @@ set(LIB bf_blenkernel ) +if(WITH_PYTHON) + add_definitions(-DWITH_PYTHON) + list(APPEND INC + ../python + ) +endif() + blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index ad88cf656ad..c816c7b8db5 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -41,6 +41,10 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + #include "atomic_ops.h" #include "intern/depsgraph.h" @@ -375,6 +379,11 @@ void deg_evaluate_on_refresh(Depsgraph *graph) graph->debug.begin_graph_evaluation(); +#ifdef WITH_PYTHON + /* Release the GIL so that Python drivers can be evaluated. See T91046. */ + BPy_BEGIN_ALLOW_THREADS; +#endif + graph->is_evaluating = true; depsgraph_ensure_view_layer(graph); /* Set up evaluation state. */ @@ -415,6 +424,10 @@ void deg_evaluate_on_refresh(Depsgraph *graph) deg_graph_clear_tags(graph); graph->is_evaluating = false; +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif + graph->debug.end_graph_evaluation(); } diff --git a/source/blender/python/generic/bpy_threads.c b/source/blender/python/generic/bpy_threads.c index 57eb3a82c44..54548b88d4f 100644 --- a/source/blender/python/generic/bpy_threads.c +++ b/source/blender/python/generic/bpy_threads.c @@ -29,10 +29,9 @@ /* analogue of PyEval_SaveThread() */ BPy_ThreadStatePtr BPY_thread_save(void) { - /* Use `_PyThreadState_UncheckedGet()`, instead of more canonical `PyGILState_Check()` or - * `PyThreadState_Get()`, to avoid a fatal error issued when a thread state is NULL (the thread - * state can be NULL when quitting Blender). */ - if (_PyThreadState_UncheckedGet()) { + /* Don't use `PyThreadState_Get()`, to avoid a fatal error issued when a thread state is NULL + * (the thread state can be NULL when quitting Blender). */ + if (PyGILState_Check()) { return (BPy_ThreadStatePtr)PyEval_SaveThread(); } return NULL; -- cgit v1.2.3 From a00507c482e28ad20813f082057d4a09061c03cc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 10 Sep 2021 19:18:13 +1000 Subject: Templates: remove masking layers from the default startup file Remove sculpt mask layer from the default cube, added in 444934632a8d8e239bc0c6d79a638ec0943152a6. --- release/datafiles/startup.blend | Bin 804804 -> 650648 bytes .../blenloader/intern/versioning_defaults.c | 6 ++++++ 2 files changed, 6 insertions(+) diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend index 20ebf5d9986..8caec075138 100644 Binary files a/release/datafiles/startup.blend and b/release/datafiles/startup.blend differ diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 82c577d11a0..ae0dbb7f808 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -52,6 +52,7 @@ #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_curveprofile.h" +#include "BKE_customdata.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -552,6 +553,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) mesh->flag |= ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME; BKE_mesh_smooth_flag_set(mesh, false); } + else { + /* Remove sculpt-mask data in default mesh objects for all non-sculpt templates. */ + CustomData_free_layers(&mesh->vdata, CD_PAINT_MASK, mesh->totvert); + CustomData_free_layers(&mesh->ldata, CD_GRID_PAINT_MASK, mesh->totloop); + } } for (Camera *camera = bmain->cameras.first; camera; camera = camera->id.next) { -- cgit v1.2.3 From 60cfdf080929aca61c37439b601b1ef85d51391a Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 Sep 2021 13:27:26 +0200 Subject: Anim: Keylist drawing optimization by using arrays. Change data structure of keylists. Reducing the balancing overhead and therefore increases performance. | **Function** | **Master** | **Patch** | |`draw_summary_channel`| 0.202105s| 0.083874s | When adding items to the keylist it will store it in a linked list. This linked list is accompanied with the length (key_len) and a `last_accessed_column`. last_accessed_column is a cursor that improve the performance when adding new items as they are mostly ordered by frame numbers. last_accessed_column is reset when a new fcurve/mask/... is added to the keylist. Before searching or array access. the listbase needs to be converted to an array. `ED_keylist_prepare_for_direct_access`. After that the caller can use `ED_keylist_find_*` or `ED_keylist_array*` functions. The internal array can also be accessed via the `ED_keylist_listbase` function. The items inside the array link to the previous/next item in the list. Reviewed By: sybren Differential Revision: https://developer.blender.org/D12052 --- source/blender/editors/animation/anim_draw.c | 1 + .../blender/editors/animation/anim_motion_paths.c | 2 + source/blender/editors/animation/keyframes_draw.c | 29 +- .../blender/editors/animation/keyframes_keylist.cc | 482 +++++++++++++++------ source/blender/editors/armature/pose_slide.c | 2 + .../blender/editors/include/ED_keyframes_keylist.h | 14 +- source/blender/editors/screen/screen_ops.c | 1 + .../blender/editors/space_action/action_select.c | 1 + 8 files changed, 397 insertions(+), 135 deletions(-) diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 6d272bfc180..993d10cf303 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -521,6 +521,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_ MaskLayer *masklay = BKE_mask_layer_active(mask); mask_to_keylist(&ads, masklay, keylist); } + ED_keylist_prepare_for_direct_access(keylist); /* TODO(jbakker): Keylists are ordered, no need to do any searching at all. */ /* find matching keyframe in the right direction */ diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index d976f5f72ad..d130941b9bc 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -329,6 +329,7 @@ static void motionpath_calculate_update_range(MPathTarget *mpt, for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) { struct AnimKeylist *keylist = ED_keylist_create(); fcurve_to_keylist(adt, fcu, keylist, 0); + ED_keylist_prepare_for_direct_access(keylist); int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame); int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame); @@ -443,6 +444,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph, action_to_keylist(adt, adt->action, mpt->keylist, 0); } } + ED_keylist_prepare_for_direct_access(mpt->keylist); if (range == ANIMVIZ_CALC_RANGE_CHANGED) { int mpt_sfra, mpt_efra; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 61918871b90..8a884c0bd5b 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -346,10 +346,12 @@ static void draw_keylist_block(const DrawKeylistUIData *ctx, const ActKeyColumn } static void draw_keylist_blocks(const DrawKeylistUIData *ctx, - const ListBase * /*ActKeyColumn*/ columns, + const ActKeyColumn *keys, + const int key_len, float ypos) { - LISTBASE_FOREACH (ActKeyColumn *, ab, columns) { + for (int i = 0; i < key_len; i++) { + const ActKeyColumn *ab = &keys[i]; draw_keylist_block(ctx, ab, ypos); } } @@ -362,13 +364,15 @@ static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *a static void draw_keylist_keys(const DrawKeylistUIData *ctx, View2D *v2d, const KeyframeShaderBindings *sh_bindings, - const ListBase * /*ActKeyColumn*/ keys, + const ActKeyColumn *keys, + const int key_len, float ypos, eSAction_Flag saction_flag) { short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE; - LISTBASE_FOREACH (ActKeyColumn *, ak, keys) { + for (int i = 0; i < key_len; i++) { + const ActKeyColumn *ak = &keys[i]; if (draw_keylist_is_visible_key(v2d, ak)) { if (ctx->show_ipo) { handle_type = ak->handle_type; @@ -469,8 +473,9 @@ static void ED_keylist_draw_list_elem_draw_blocks(AnimKeylistDrawListElem *elem, DrawKeylistUIData ctx; draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); - const ListBase *keys = ED_keylist_listbase(elem->keylist); - draw_keylist_blocks(&ctx, keys, elem->ypos); + const int key_len = ED_keylist_array_len(elem->keylist); + const ActKeyColumn *keys = ED_keylist_array(elem->keylist); + draw_keylist_blocks(&ctx, keys, key_len, elem->ypos); } static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, @@ -479,8 +484,15 @@ static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem, { DrawKeylistUIData ctx; draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag); - const ListBase *keys = ED_keylist_listbase(elem->keylist); - draw_keylist_keys(&ctx, v2d, sh_bindings, keys, elem->ypos, elem->saction_flag); + + const int key_len = ED_keylist_array_len(elem->keylist); + const ActKeyColumn *keys = ED_keylist_array(elem->keylist); + draw_keylist_keys(&ctx, v2d, sh_bindings, keys, key_len, elem->ypos, elem->saction_flag); +} + +static void ED_keylist_draw_list_elem_prepare_for_drawing(AnimKeylistDrawListElem *elem) +{ + ED_keylist_prepare_for_direct_access(elem->keylist); } typedef struct AnimKeylistDrawList { @@ -496,6 +508,7 @@ static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { ED_keylist_draw_list_elem_build_keylist(elem); + ED_keylist_draw_list_elem_prepare_for_drawing(elem); } } diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index f6ade11a517..c1a18196a3a 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -23,15 +23,20 @@ /* System includes ----------------------------------------------------- */ +#include #include #include #include #include +#include +#include #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_dlrbTree.h" #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_range.h" #include "BLI_utildefines.h" @@ -50,117 +55,294 @@ extern "C" { /* *************************** Keyframe Processing *************************** */ -struct AnimKeylist { - DLRBT_Tree keys; -}; +/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ + +BLI_INLINE bool is_cfra_eq(const float a, const float b) +{ + return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); +} -static void ED_keylist_init(AnimKeylist *keylist) +BLI_INLINE bool is_cfra_lt(const float a, const float b) { - BLI_dlrbTree_init(&keylist->keys); + return (b - a) > BEZT_BINARYSEARCH_THRESH; } +/* --------------- */ + +struct AnimKeylist { + /* Number of ActKeyColumn's in the keylist. */ + size_t column_len = 0; + + bool is_runtime_initialized = false; + + /* Before initializing the runtime, the key_columns list base is used to quickly add columns. + * Contains `ActKeyColumn`. Should not be used after runtime is initialized. */ + ListBase /* ActKeyColumn */ key_columns; + /* Last accessed column in the key_columns list base. Inserting columns are typically done in + * order. The last accessed column is used as starting point to search for a location to add or + * update the next column.*/ + std::optional last_accessed_column = std::nullopt; + + struct { + /* When initializing the runtime the columns from the list base `AnimKeyList.key_columns` are + * transferred to an array to support binary searching and index based access. */ + blender::Array key_columns; + /* Wrapper around runtime.key_columns so it can still be accessed as a ListBase. Elements are + * owned by runtime.key_columns. */ + ListBase /* ActKeyColumn */ list_wrapper; + } runtime; + + AnimKeylist() + { + BLI_listbase_clear(&this->key_columns); + BLI_listbase_clear(&this->runtime.list_wrapper); + } + + ~AnimKeylist() + { + BLI_freelistN(&this->key_columns); + BLI_listbase_clear(&this->runtime.list_wrapper); + } + +#ifdef WITH_CXX_GUARDEDALLOC + MEM_CXX_CLASS_ALLOC_FUNCS("editors:AnimKeylist") +#endif +}; + AnimKeylist *ED_keylist_create(void) { - AnimKeylist *keylist = static_cast(MEM_callocN(sizeof(AnimKeylist), __func__)); - ED_keylist_init(keylist); + AnimKeylist *keylist = new AnimKeylist(); return keylist; } void ED_keylist_free(AnimKeylist *keylist) { BLI_assert(keylist); - BLI_dlrbTree_free(&keylist->keys); - MEM_freeN(keylist); + delete keylist; } -const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra) +static void ED_keylist_convert_key_columns_to_array(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &cfra); + size_t index; + LISTBASE_FOREACH_INDEX (ActKeyColumn *, key, &keylist->key_columns, index) { + keylist->runtime.key_columns[index] = *key; + } } -const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra) +static void ED_keylist_runtime_update_key_column_next_prev(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra); + for (size_t index = 0; index < keylist->column_len; index++) { + const bool is_first = (index == 0); + keylist->runtime.key_columns[index].prev = is_first ? nullptr : + &keylist->runtime.key_columns[index - 1]; + const bool is_last = (index == keylist->column_len - 1); + keylist->runtime.key_columns[index].next = is_last ? nullptr : + &keylist->runtime.key_columns[index + 1]; + } } -const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra) +static void ED_keylist_runtime_init_listbase(AnimKeylist *keylist) { - return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra); + if (ED_keylist_is_empty(keylist)) { + BLI_listbase_clear(&keylist->runtime.list_wrapper); + return; + } + + keylist->runtime.list_wrapper.first = &keylist->runtime.key_columns[0]; + keylist->runtime.list_wrapper.last = &keylist->runtime.key_columns[keylist->column_len - 1]; } -/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check - * boundary of `max_fra`. */ -const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, - const Range2f frame_range) +static void ED_keylist_runtime_init(AnimKeylist *keylist) { - for (const ActKeyColumn *ak = static_cast(keylist->keys.root); ak; - ak = static_cast((ak->cfra < frame_range.min) ? ak->right : - ak->left)) { - if (range2f_in_range(&frame_range, ak->cfra)) { - return ak; - } + BLI_assert(!keylist->is_runtime_initialized); + + keylist->runtime.key_columns = blender::Array(keylist->column_len); + + /* Convert linked list to array to support fast searching. */ + ED_keylist_convert_key_columns_to_array(keylist); + /* Ensure that the array can also be used as a listbase for external usages. */ + ED_keylist_runtime_update_key_column_next_prev(keylist); + ED_keylist_runtime_init_listbase(keylist); + + keylist->is_runtime_initialized = true; +} + +static void ED_keylist_reset_last_accessed(AnimKeylist *keylist) +{ + BLI_assert(!keylist->is_runtime_initialized); + keylist->last_accessed_column.reset(); +} + +void ED_keylist_prepare_for_direct_access(AnimKeylist *keylist) +{ + if (keylist->is_runtime_initialized) { + return; + } + ED_keylist_runtime_init(keylist); +} + +static const ActKeyColumn *ED_keylist_find_lower_bound(const AnimKeylist *keylist, + const float cfra) +{ + BLI_assert(!ED_keylist_is_empty(keylist)); + const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + ActKeyColumn value; + value.cfra = cfra; + + const ActKeyColumn *found_column = std::lower_bound( + begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) { + return is_cfra_lt(column.cfra, other.cfra); + }); + return found_column; +} + +static const ActKeyColumn *ED_keylist_find_upper_bound(const AnimKeylist *keylist, + const float cfra) +{ + BLI_assert(!ED_keylist_is_empty(keylist)); + const ActKeyColumn *begin = std::begin(keylist->runtime.key_columns); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + ActKeyColumn value; + value.cfra = cfra; + + const ActKeyColumn *found_column = std::upper_bound( + begin, end, value, [](const ActKeyColumn &column, const ActKeyColumn &other) { + return is_cfra_lt(column.cfra, other.cfra); + }); + return found_column; +} + +const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, const float cfra) +{ + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra); + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (found_column == end) { + return nullptr; + } + if (is_cfra_eq(found_column->cfra, cfra)) { + return found_column; } return nullptr; } -bool ED_keylist_is_empty(const struct AnimKeylist *keylist) +const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, const float cfra) { - return keylist->keys.root == nullptr; + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *found_column = ED_keylist_find_upper_bound(keylist, cfra); + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (found_column == end) { + return nullptr; + } + return found_column; } -const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) +const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, const float cfra) { - return (ListBase *)&keylist->keys; + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); + + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + const ActKeyColumn *found_column = ED_keylist_find_lower_bound(keylist, cfra); + + if (found_column == end) { + /* Nothing found, return the last item. */ + return end - 1; + } + + const ActKeyColumn *prev_column = found_column->prev; + return prev_column; } -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist, + const Range2f frame_range) { - BLI_assert(r_frame_range); + BLI_assert_msg(keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before searching."); if (ED_keylist_is_empty(keylist)) { - return false; + return nullptr; } - const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first; - r_frame_range->min = first_column->cfra; + const ActKeyColumn *column = ED_keylist_find_lower_bound(keylist, frame_range.min); + const ActKeyColumn *end = std::end(keylist->runtime.key_columns); + if (column == end) { + return nullptr; + } + if (column->cfra >= frame_range.max) { + return nullptr; + } + return column; +} - const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last; - r_frame_range->max = last_column->cfra; +const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist) +{ + BLI_assert_msg( + keylist->is_runtime_initialized, + "ED_keylist_prepare_for_direct_access needs to be called before accessing array."); + return keylist->runtime.key_columns.data(); +} - return true; +int64_t ED_keylist_array_len(const struct AnimKeylist *keylist) +{ + return keylist->column_len; } -/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ -BLI_INLINE bool is_cfra_eq(const float a, const float b) +bool ED_keylist_is_empty(const struct AnimKeylist *keylist) { - return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); + return keylist->column_len == 0; } -BLI_INLINE bool is_cfra_lt(const float a, const float b) +const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) { - return (b - a) > BEZT_BINARYSEARCH_THRESH; + if (keylist->is_runtime_initialized) { + return &keylist->runtime.list_wrapper; + } + return &keylist->key_columns; } -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ -short compare_ak_cfraPtr(void *node, void *data) +bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const float *cframe = static_cast(data); - const float val = *cframe; + BLI_assert(r_frame_range); - if (is_cfra_eq(val, ak->cfra)) { - return 0; + if (ED_keylist_is_empty(keylist)) { + return false; } - if (val < ak->cfra) { - return -1; + const ActKeyColumn *first_column; + const ActKeyColumn *last_column; + if (keylist->is_runtime_initialized) { + first_column = &keylist->runtime.key_columns[0]; + last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; } - return 1; -} + else { + first_column = static_cast(keylist->key_columns.first); + last_column = static_cast(keylist->key_columns.last); + } + r_frame_range->min = first_column->cfra; + r_frame_range->max = last_column->cfra; -/* --------------- */ + return true; +} /* Set of references to three logically adjacent keys. */ struct BezTripleChain { @@ -243,16 +425,8 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain) return KEYFRAME_EXTREME_NONE; } -/* Comparator callback used for ActKeyColumns and BezTripleChain */ -static short compare_ak_bezt(void *node, void *data) -{ - BezTripleChain *chain = static_cast(data); - - return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); -} - /* New node callback used for building ActKeyColumns from BezTripleChain */ -static DLRBT_Node *nalloc_ak_bezt(void *data) +static ActKeyColumn *nalloc_ak_bezt(void *data) { ActKeyColumn *ak = static_cast( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn")); @@ -269,13 +443,12 @@ static DLRBT_Node *nalloc_ak_bezt(void *data) /* count keyframes in this column */ ak->totkey = 1; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from BezTripleChain */ -static void nupdate_ak_bezt(void *node, void *data) +static void nupdate_ak_bezt(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = static_cast(node); const BezTripleChain *chain = static_cast(data); const BezTriple *bezt = chain->cur; @@ -312,17 +485,8 @@ static void nupdate_ak_bezt(void *node, void *data) /* ......... */ -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_gpframe(void *node, void *data) -{ - const bGPDframe *gpf = (bGPDframe *)data; - - float frame = gpf->framenum; - return compare_ak_cfraPtr(node, &frame); -} - /* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_gpframe(void *data) +static ActKeyColumn *nalloc_ak_gpframe(void *data) { ActKeyColumn *ak = static_cast( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); @@ -340,14 +504,13 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data) ak->block.sel = ak->sel; ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_gpframe(void *node, void *data) +static void nupdate_ak_gpframe(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const bGPDframe *gpf = (bGPDframe *)data; + bGPDframe *gpf = (bGPDframe *)data; /* set selection status and 'touched' status */ if (gpf->flag & GP_FRAME_SELECT) { @@ -366,17 +529,8 @@ static void nupdate_ak_gpframe(void *node, void *data) /* ......... */ -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_masklayshape(void *node, void *data) -{ - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; - - float frame = masklay_shape->frame; - return compare_ak_cfraPtr(node, &frame); -} - /* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_masklayshape(void *data) +static ActKeyColumn *nalloc_ak_masklayshape(void *data) { ActKeyColumn *ak = static_cast( MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF")); @@ -389,14 +543,13 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data) /* count keyframes in this column */ ak->totkey = 1; - return (DLRBT_Node *)ak; + return ak; } /* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_masklayshape(void *node, void *data) +static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data) { - ActKeyColumn *ak = (ActKeyColumn *)node; - const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data; + MaskLayerShape *masklay_shape = (MaskLayerShape *)data; /* set selection status and 'touched' status */ if (masklay_shape->flag & MASK_SHAPE_SELECT) { @@ -408,6 +561,95 @@ static void nupdate_ak_masklayshape(void *node, void *data) } /* --------------- */ +using KeylistCreateColumnFunction = std::function; +using KeylistUpdateColumnFunction = std::function; + +/* `ED_keylist_find_neighbour_front_to_back` is called before the runtime can be initialized so we + * cannot use bin searching. */ +static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *cursor, float cfra) +{ + while (cursor->next && cursor->next->cfra <= cfra) { + cursor = cursor->next; + } + return cursor; +} + +/* `ED_keylist_find_neighbour_back_to_front` is called before the runtime can be initialized so we + * cannot use bin searching. */ +static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *cursor, float cfra) +{ + while (cursor->prev && cursor->prev->cfra >= cfra) { + cursor = cursor->prev; + } + return cursor; +} + +/* + * `ED_keylist_find_exact_or_neighbour_column` is called before the runtime can be initialized so + * we cannot use bin searching. + * + * This function is called to add or update columns in the keylist. + * Typically columns are sorted by frame number so keeping track of the last_accessed_column + * reduces searching. + */ +static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keylist, float cfra) +{ + BLI_assert(!keylist->is_runtime_initialized); + if (ED_keylist_is_empty(keylist)) { + return nullptr; + } + + ActKeyColumn *cursor = keylist->last_accessed_column.value_or( + static_cast(keylist->key_columns.first)); + if (!is_cfra_eq(cursor->cfra, cfra)) { + const bool walking_direction_front_to_back = cursor->cfra <= cfra; + if (walking_direction_front_to_back) { + cursor = ED_keylist_find_neighbour_front_to_back(cursor, cfra); + } + else { + cursor = ED_keylist_find_neighbour_back_to_front(cursor, cfra); + } + } + + keylist->last_accessed_column = cursor; + return cursor; +} + +static void ED_keylist_add_or_update_column(AnimKeylist *keylist, + float cfra, + KeylistCreateColumnFunction create_func, + KeylistUpdateColumnFunction update_func, + void *userdata) +{ + BLI_assert_msg( + !keylist->is_runtime_initialized, + "Modifying AnimKeylist isn't allowed after runtime is initialized " + "keylist->key_columns/columns_len will get out of sync with runtime.key_columns."); + if (ED_keylist_is_empty(keylist)) { + ActKeyColumn *key_column = create_func(userdata); + BLI_addhead(&keylist->key_columns, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + return; + } + + ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbour_column(keylist, cfra); + if (is_cfra_eq(nearest->cfra, cfra)) { + update_func(nearest, userdata); + } + else if (is_cfra_lt(nearest->cfra, cfra)) { + ActKeyColumn *key_column = create_func(userdata); + BLI_insertlinkafter(&keylist->key_columns, nearest, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + } + else { + ActKeyColumn *key_column = create_func(userdata); + BLI_insertlinkbefore(&keylist->key_columns, nearest, key_column); + keylist->column_len += 1; + keylist->last_accessed_column = key_column; + } +} /* Add the given BezTriple to the given 'list' of Keyframes */ static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt) @@ -416,7 +658,8 @@ static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *be return; } - BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); + float cfra = bezt->cur->vec[1][0]; + ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_bezt, nupdate_ak_bezt, bezt); } /* Add the given GPencil Frame to the given 'list' of Keyframes */ @@ -426,7 +669,8 @@ static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf) return; } - BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); + float cfra = gpf->framenum; + ED_keylist_add_or_update_column(keylist, cfra, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); } /* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ @@ -436,11 +680,9 @@ static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape return; } - BLI_dlrbTree_add(&keylist->keys, - compare_ak_masklayshape, - nalloc_ak_masklayshape, - nupdate_ak_masklayshape, - masklay_shape); + float cfra = masklay_shape->frame; + ED_keylist_add_or_update_column( + keylist, cfra, nalloc_ak_masklayshape, nupdate_ak_masklayshape, masklay_shape); } /* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ @@ -476,7 +718,7 @@ static void compute_keyblock_data(ActKeyBlockInfo *info, hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && IS_EQF(prev->vec[1][1], prev->vec[2][1]); } - /* This interpolation type induces movement even between identical keys. */ + /* This interpolation type induces movement even between identical columns. */ else { hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); } @@ -514,7 +756,7 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { - ActKeyColumn *col = static_cast(keylist->keys.first); + ActKeyColumn *col = static_cast(keylist->key_columns.first); if (bezt && bezt_len >= 2) { ActKeyBlockInfo block; @@ -532,19 +774,15 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbour_column(keylist, col->cfra); - if (newcol != nullptr) { - col = newcol; + BLI_assert(newcol); + BLI_assert(newcol->cfra == col->cfra); - /* The previous keyblock is garbage too. */ - if (col->prev != nullptr) { - add_keyblock_info(col->prev, &dummy_keyblock); - } - } - else { - BLI_assert(false); + col = newcol; + /* The previous keyblock is garbage too. */ + if (col->prev != nullptr) { + add_keyblock_info(col->prev, &dummy_keyblock); } } @@ -577,20 +815,17 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co */ static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len) { - /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(&keylist->keys); - /* Find the curve count */ int max_curve = 0; - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) { max_curve = MAX2(max_curve, col->totcurve); } /* Propagate blocks to inserted keys */ ActKeyColumn *prev_ready = nullptr; - LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) { + LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->key_columns) { /* Pre-existing column. */ if (col->totcurve > 0) { prev_ready = col; @@ -775,6 +1010,7 @@ void cachefile_to_keylist(bDopeSheet *ads, void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag) { if (fcu && fcu->totvert && fcu->bezt) { + ED_keylist_reset_last_accessed(keylist); /* apply NLA-mapping (if applicable) */ if (adt) { ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false); @@ -790,7 +1026,7 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const i for (int v = 0; v < fcu->totvert; v++) { chain.cur = &fcu->bezt[v]; - /* Neighbor keys, accounting for being cyclic. */ + /* Neighbor columns, accounting for being cyclic. */ if (do_extremes) { chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : @@ -856,6 +1092,7 @@ void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, con void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist) { if (gpl && keylist) { + ED_keylist_reset_last_accessed(keylist); /* Although the frames should already be in an ordered list, * they are not suitable for displaying yet. */ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { @@ -869,6 +1106,7 @@ void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylis void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist) { if (masklay && keylist) { + ED_keylist_reset_last_accessed(keylist); LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { add_masklay_to_keycolumns_list(keylist, masklay_shape); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index bc5cbd92deb..c02644cde39 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -976,6 +976,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * } /* Cancel if no keyframes found. */ + ED_keylist_prepare_for_direct_access(pso->keylist); if (ED_keylist_is_empty(pso->keylist)) { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); pose_slide_exit(C, op); @@ -1712,6 +1713,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st FCurve *fcu = (FCurve *)ld->data; fcurve_to_keylist(adt, fcu, keylist, 0); } + ED_keylist_prepare_for_direct_access(keylist); /* Find the long keyframe (i.e. hold), and hence obtain the endFrame value * - the best case would be one that starts on the frame itself diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 3a9750c1206..4194444ca0f 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -139,14 +139,20 @@ typedef enum eKeyframeExtremeDrawOpts { struct AnimKeylist *ED_keylist_create(void); void ED_keylist_free(struct AnimKeylist *keylist); -const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra); -const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra); -const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra); +void ED_keylist_prepare_for_direct_access(struct AnimKeylist *keylist); +const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, + const float cfra); +const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, + const float cfra); +const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, + const float cfra); const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist, const Range2f frame_range); bool ED_keylist_is_empty(const struct AnimKeylist *keylist); const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); +const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist); +int64_t ED_keylist_array_len(const struct AnimKeylist *keylist); /* Key-data Generation --------------- */ @@ -197,8 +203,6 @@ void mask_to_keylist(struct bDopeSheet *ads, struct AnimKeylist *keylist); /* ActKeyColumn API ---------------- */ -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -short compare_ak_cfraPtr(void *node, void *data); /* Checks if ActKeyColumn has any block data */ bool actkeyblock_is_valid(const ActKeyColumn *ac); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 5d6f21f4854..3efe4ae85d5 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3107,6 +3107,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) mask_to_keylist(&ads, masklay, keylist); } } + ED_keylist_prepare_for_direct_access(keylist); /* find matching keyframe in the right direction */ const ActKeyColumn *ak; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 9dcfc626a50..a5e75e31e38 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -162,6 +162,7 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac, struct AnimKeylist *keylist = ED_keylist_create(); actkeys_list_element_to_keylist(ac, keylist, ale); + ED_keylist_prepare_for_direct_access(keylist); AnimData *adt = ANIM_nla_mapping_get(ac, ale); -- cgit v1.2.3 From a1167e910a22077a6738edc3ee8e57ab4d5d62dd Mon Sep 17 00:00:00 2001 From: Omar Emara Date: Fri, 10 Sep 2021 14:25:32 +0200 Subject: BLI: Add Cycles compatible Perlin noise This patch adds new Perlin noise functions to BLI. The noises are compatible with the shading texture noises in EEVEE, SVM, and OSL. The existing Jenkins hash functions couldn't be used because they are not compatible with the shading implementations and an attempt at adjusting the implementation will break compatibility in various areas of Blender. So the simplest approach is to reimplement the relevant hashing functions inside the noise module itself. Additionally, this patch also adds a minimal float4 structure to use in the interface of the noise functions. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12443 --- source/blender/blenlib/BLI_float4.hh | 86 ++++ source/blender/blenlib/BLI_noise.hh | 72 ++++ source/blender/blenlib/CMakeLists.txt | 3 + source/blender/blenlib/intern/noise.cc | 693 +++++++++++++++++++++++++++++++++ 4 files changed, 854 insertions(+) create mode 100644 source/blender/blenlib/BLI_float4.hh create mode 100644 source/blender/blenlib/BLI_noise.hh create mode 100644 source/blender/blenlib/intern/noise.cc diff --git a/source/blender/blenlib/BLI_float4.hh b/source/blender/blenlib/BLI_float4.hh new file mode 100644 index 00000000000..b1feee3121b --- /dev/null +++ b/source/blender/blenlib/BLI_float4.hh @@ -0,0 +1,86 @@ +/* + * 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. + */ + +#pragma once + +namespace blender { + +struct float4 { + float x, y, z, w; + + float4() = default; + + float4(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]}, w{ptr[3]} + { + } + + explicit float4(float value) : x(value), y(value), z(value), w(value) + { + } + + explicit float4(int value) : x(value), y(value), z(value), w(value) + { + } + + float4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) + { + } + + operator float *() + { + return &x; + } + + operator const float *() const + { + return &x; + } + + float4 &operator+=(const float4 &other) + { + x += other.x; + y += other.y; + z += other.z; + w += other.w; + return *this; + } + + friend float4 operator+(const float4 &a, const float4 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; + } + + float4 &operator*=(float factor) + { + x *= factor; + y *= factor; + z *= factor; + w *= factor; + return *this; + } + + friend float4 operator*(const float4 &a, float b) + { + return {a.x * b, a.y * b, a.z * b, a.w * b}; + } + + friend float4 operator*(float a, const float4 &b) + { + return b * a; + } +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh new file mode 100644 index 00000000000..760ff082d06 --- /dev/null +++ b/source/blender/blenlib/BLI_noise.hh @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#pragma once + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" + +namespace blender::noise { + +/* Perlin noise in the range [-1, 1]. */ + +float perlin_signed(float position); +float perlin_signed(float2 position); +float perlin_signed(float3 position); +float perlin_signed(float4 position); + +/* Perlin noise in the range [0, 1]. */ + +float perlin(float position); +float perlin(float2 position); +float perlin(float3 position); +float perlin(float4 position); + +/* Fractal perlin noise in the range [0, 1]. */ + +float perlin_fractal(float position, float octaves, float roughness); +float perlin_fractal(float2 position, float octaves, float roughness); +float perlin_fractal(float3 position, float octaves, float roughness); +float perlin_fractal(float4 position, float octaves, float roughness); + +/* Positive distorted fractal perlin noise. */ + +float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion); +float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion); + +/* Positive distorted fractal perlin noise that outputs a float3. */ + +float3 perlin_float3_fractal_distorted(float position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float2 position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float3 position, + float octaves, + float roughness, + float distortion); +float3 perlin_float3_fractal_distorted(float4 position, + float octaves, + float roughness, + float distortion); + +} // namespace blender::noise diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 24178535068..fc058793d11 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRC intern/mesh_boolean.cc intern/mesh_intersect.cc intern/noise.c + intern/noise.cc intern/path_util.c intern/polyfill_2d.c intern/polyfill_2d_beautify.c @@ -203,6 +204,7 @@ set(SRC BLI_filereader.h BLI_float2.hh BLI_float3.hh + BLI_float4.hh BLI_float4x4.hh BLI_fnmatch.h BLI_function_ref.hh @@ -264,6 +266,7 @@ set(SRC BLI_mpq3.hh BLI_multi_value_map.hh BLI_noise.h + BLI_noise.hh BLI_path_util.h BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc new file mode 100644 index 00000000000..c057c12e543 --- /dev/null +++ b/source/blender/blenlib/intern/noise.cc @@ -0,0 +1,693 @@ +/* + * Adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * 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. + */ + +#include +#include + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_noise.hh" +#include "BLI_utildefines.h" + +namespace blender::noise { +/* ------------------------------ + * Jenkins Lookup3 Hash Functions + * ------------------------------ + * + * https://burtleburtle.net/bob/c/lookup3.c + * + */ + +BLI_INLINE uint32_t hash_bit_rotate(uint32_t x, uint32_t k) +{ + return (x << k) | (x >> (32 - k)); +} + +BLI_INLINE void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c) +{ + a -= c; + a ^= hash_bit_rotate(c, 4); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 6); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 8); + b += a; + a -= c; + a ^= hash_bit_rotate(c, 16); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 19); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 4); + b += a; +} + +BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) +{ + c ^= b; + c -= hash_bit_rotate(b, 14); + a ^= c; + a -= hash_bit_rotate(c, 11); + b ^= a; + b -= hash_bit_rotate(a, 25); + c ^= b; + c -= hash_bit_rotate(b, 16); + a ^= c; + a -= hash_bit_rotate(c, 4); + b ^= a; + b -= hash_bit_rotate(a, 14); + c ^= b; + c -= hash_bit_rotate(b, 24); +} + +BLI_INLINE uint32_t hash(uint32_t kx) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (1 << 2) + 13; + + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (2 << 2) + 13; + + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (3 << 2) + 13; + + c += kz; + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (4 << 2) + 13; + + a += kx; + b += ky; + c += kz; + hash_bit_mix(a, b, c); + + a += kw; + hash_bit_final(a, b, c); + + return c; +} + +/* Hashing a number of uint32_t into a float in the range [0, 1]. */ + +BLI_INLINE float hash_to_float(uint32_t kx) +{ + return static_cast(hash(kx)) / static_cast(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky) +{ + return static_cast(hash(kx, ky)) / static_cast(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) +{ + return static_cast(hash(kx, ky, kz)) / static_cast(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + return static_cast(hash(kx, ky, kz, kw)) / static_cast(0xFFFFFFFFu); +} + +/* Hashing a number of floats into a float in the range [0, 1]. */ + +BLI_INLINE uint32_t float_as_uint(float f) +{ + union { + uint32_t i; + float f; + } u; + u.f = f; + return u.i; +} + +BLI_INLINE float hash_to_float(float k) +{ + return hash_to_float(float_as_uint(k)); +} + +BLI_INLINE float hash_to_float(float2 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y)); +} + +BLI_INLINE float hash_to_float(float3 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); +} + +BLI_INLINE float hash_to_float(float4 k) +{ + return hash_to_float( + float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); +} + +/* ------------ + * Perlin Noise + * ------------ + * + * Perlin, Ken. "Improving noise." Proceedings of the 29th annual conference on Computer graphics + * and interactive techniques. 2002. + * + * This implementation is functionally identical to the implementations in EEVEE, OSL, and SVM. So + * any changes should be applied in all relevant implementations. + */ + +/* Linear Interpolation. */ +BLI_INLINE float mix(float v0, float v1, float x) +{ + return (1 - x) * v0 + x * v1; +} + +/* Bilinear Interpolation: + * + * v2 v3 + * @ + + + + @ y + * + + ^ + * + + | + * + + | + * @ + + + + @ @------> x + * v0 v1 + * + */ +BLI_INLINE float mix(float v0, float v1, float v2, float v3, float x, float y) +{ + float x1 = 1.0 - x; + return (1.0 - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x); +} + +/* Trilinear Interpolation: + * + * v6 v7 + * @ + + + + + + @ + * +\ +\ + * + \ + \ + * + \ + \ + * + \ v4 + \ v5 + * + @ + + + +++ + @ z + * + + + + y ^ + * v2 @ + +++ + + + @ v3 + \ | + * \ + \ + \ | + * \ + \ + \| + * \ + \ + +---------> x + * \+ \+ + * @ + + + + + + @ + * v0 v1 + */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float x, + float y, + float z) +{ + float x1 = 1.0 - x; + float y1 = 1.0 - y; + float z1 = 1.0 - z; + return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + + z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); +} + +/* Quadrilinear Interpolation. */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float v8, + float v9, + float v10, + float v11, + float v12, + float v13, + float v14, + float v15, + float x, + float y, + float z, + float w) +{ + return mix(mix(v0, v1, v2, v3, v4, v5, v6, v7, x, y, z), + mix(v8, v9, v10, v11, v12, v13, v14, v15, x, y, z), + w); +} + +BLI_INLINE float fade(float t) +{ + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); +} + +BLI_INLINE float negate_if(float value, uint32_t condition) +{ + return (condition != 0u) ? -value : value; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x) +{ + uint32_t h = hash & 15u; + float g = 1u + (h & 7u); + return negate_if(g, h & 8u) * x; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y) +{ + uint32_t h = hash & 7u; + float u = h < 4u ? x : y; + float v = 2.0 * (h < 4u ? y : x); + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z) +{ + uint32_t h = hash & 15u; + float u = h < 8u ? x : y; + float vt = ((h == 12u) || (h == 14u)) ? x : z; + float v = h < 4u ? y : vt; + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z, float w) +{ + uint32_t h = hash & 31u; + float u = h < 24u ? x : y; + float v = h < 16u ? y : z; + float s = h < 8u ? z : w; + return negate_if(u, h & 1u) + negate_if(v, h & 2u) + negate_if(s, h & 4u); +} + +BLI_INLINE float floor_fraction(float x, int &i) +{ + i = (int)x - ((x < 0) ? 1 : 0); + return x - i; +} + +BLI_INLINE float perlin_noise(float position) +{ + int X; + + float fx = floor_fraction(position, X); + + float u = fade(fx); + + float r = mix(noise_grad(hash(X), fx), noise_grad(hash(X + 1), fx - 1.0), u); + + return r; +} + +BLI_INLINE float perlin_noise(float2 position) +{ + int X, Y; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + + float u = fade(fx); + float v = fade(fy); + + float r = mix(noise_grad(hash(X, Y), fx, fy), + noise_grad(hash(X + 1, Y), fx - 1.0, fy), + noise_grad(hash(X, Y + 1), fx, fy - 1.0), + noise_grad(hash(X + 1, Y + 1), fx - 1.0, fy - 1.0), + u, + v); + + return r; +} + +BLI_INLINE float perlin_noise(float3 position) +{ + int X, Y, Z; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + + float u = fade(fx); + float v = fade(fy); + float w = fade(fz); + + float r = mix(noise_grad(hash(X, Y, Z), fx, fy, fz), + noise_grad(hash(X + 1, Y, Z), fx - 1, fy, fz), + noise_grad(hash(X, Y + 1, Z), fx, fy - 1, fz), + noise_grad(hash(X + 1, Y + 1, Z), fx - 1, fy - 1, fz), + noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1), + noise_grad(hash(X + 1, Y, Z + 1), fx - 1, fy, fz - 1), + noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1, fz - 1), + noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1), + u, + v, + w); + + return r; +} + +BLI_INLINE float perlin_noise(float4 position) +{ + int X, Y, Z, W; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + float fw = floor_fraction(position.w, W); + + float u = fade(fx); + float v = fade(fy); + float t = fade(fz); + float s = fade(fw); + + float r = mix( + noise_grad(hash(X, Y, Z, W), fx, fy, fz, fw), + noise_grad(hash(X + 1, Y, Z, W), fx - 1.0, fy, fz, fw), + noise_grad(hash(X, Y + 1, Z, W), fx, fy - 1.0, fz, fw), + noise_grad(hash(X + 1, Y + 1, Z, W), fx - 1.0, fy - 1.0, fz, fw), + noise_grad(hash(X, Y, Z + 1, W), fx, fy, fz - 1.0, fw), + noise_grad(hash(X + 1, Y, Z + 1, W), fx - 1.0, fy, fz - 1.0, fw), + noise_grad(hash(X, Y + 1, Z + 1, W), fx, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X + 1, Y + 1, Z + 1, W), fx - 1.0, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X, Y, Z, W + 1), fx, fy, fz, fw - 1.0), + noise_grad(hash(X + 1, Y, Z, W + 1), fx - 1.0, fy, fz, fw - 1.0), + noise_grad(hash(X, Y + 1, Z, W + 1), fx, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z, W + 1), fx - 1.0, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X, Y, Z + 1, W + 1), fx, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y, Z + 1, W + 1), fx - 1.0, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X, Y + 1, Z + 1, W + 1), fx, fy - 1.0, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z + 1, W + 1), fx - 1.0, fy - 1.0, fz - 1.0, fw - 1.0), + u, + v, + t, + s); + + return r; +} + +/* Signed versions of perlin noise in the range [-1, 1]. The scale values were computed + * experimentally by the OSL developers to remap the noise output to the correct range. */ + +float perlin_signed(float position) +{ + return perlin_noise(position) * 0.2500f; +} + +float perlin_signed(float2 position) +{ + return perlin_noise(position) * 0.6616f; +} + +float perlin_signed(float3 position) +{ + return perlin_noise(position) * 0.9820f; +} + +float perlin_signed(float4 position) +{ + return perlin_noise(position) * 0.8344f; +} + +/* Positive versions of perlin noise in the range [0, 1]. */ + +float perlin(float position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float2 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float3 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float4 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +/* Positive fractal perlin noise. */ + +template float perlin_fractal_template(T position, float octaves, float roughness) +{ + float fscale = 1.0f; + float amp = 1.0f; + float maxamp = 0.0f; + float sum = 0.0f; + octaves = CLAMPIS(octaves, 0.0f, 16.0f); + int n = static_cast(octaves); + for (int i = 0; i <= n; i++) { + float t = perlin(fscale * position); + sum += t * amp; + maxamp += amp; + amp *= CLAMPIS(roughness, 0.0f, 1.0f); + fscale *= 2.0f; + } + float rmd = octaves - std::floor(octaves); + if (rmd == 0.0f) { + return sum / maxamp; + } + + float t = perlin(fscale * position); + float sum2 = sum + t * amp; + sum /= maxamp; + sum2 /= maxamp + amp; + return (1.0f - rmd) * sum + rmd * sum2; +} + +float perlin_fractal(float position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float2 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float3 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float4 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +/* The following offset functions generate random offsets to be added to + * positions to act as a seed since the noise functions don't have seed values. + * The offset's components are in the range [100, 200], not too high to cause + * bad precision and not too small to be noticeable. We use float seed because + * OSL only support float hashes and we need to maintain compatibility with it. + */ + +BLI_INLINE float random_float_offset(float seed) +{ + return 100.0f + hash_to_float(seed) * 100.0f; +} + +BLI_INLINE float2 random_float2_offset(float seed) +{ + return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f); +} + +BLI_INLINE float3 random_float3_offset(float seed) +{ + return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f); +} + +BLI_INLINE float4 random_float4_offset(float seed) +{ + return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f); +} + +/* Perlin noises to be added to the position to distort other noises. */ + +BLI_INLINE float perlin_distortion(float position, float strength) +{ + return perlin_signed(position + random_float_offset(0.0)) * strength; +} + +BLI_INLINE float2 perlin_distortion(float2 position, float strength) +{ + return float2(perlin_signed(position + random_float2_offset(0.0f)) * strength, + perlin_signed(position + random_float2_offset(1.0f)) * strength); +} + +BLI_INLINE float3 perlin_distortion(float3 position, float strength) +{ + return float3(perlin_signed(position + random_float3_offset(0.0f)) * strength, + perlin_signed(position + random_float3_offset(1.0f)) * strength, + perlin_signed(position + random_float3_offset(2.0f)) * strength); +} + +BLI_INLINE float4 perlin_distortion(float4 position, float strength) +{ + return float4(perlin_signed(position + random_float4_offset(0.0f)) * strength, + perlin_signed(position + random_float4_offset(1.0f)) * strength, + perlin_signed(position + random_float4_offset(2.0f)) * strength, + perlin_signed(position + random_float4_offset(3.0f)) * strength); +} + +/* Positive distorted fractal perlin noise. */ + +float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +/* Positive distorted fractal perlin noise that outputs a float3. The arbitrary seeds are for + * compatibility with shading functions. */ + +float3 perlin_float3_fractal_distorted(float position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float_offset(1.0f), octaves, roughness), + perlin_fractal(position + random_float_offset(2.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float2 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float2_offset(2.0f), octaves, roughness), + perlin_fractal(position + random_float2_offset(3.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float3 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float3_offset(3.0f), octaves, roughness), + perlin_fractal(position + random_float3_offset(4.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float4 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float4_offset(4.0f), octaves, roughness), + perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); +} + +} // namespace blender::noise -- cgit v1.2.3 From 7f1fe10595065128aab2a4aea4bc9c46e155053c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 Sep 2021 14:26:21 +0200 Subject: T78995: Enable keylist threaded drawing. This enabled multithreaded building of the keys that needs to be drawn in the timeline (and other action editors). On an AMD Ryzen 3800 using a mocap data test file (available in patch) the performance went from 2fps to 8fps. The performance increase depends on the number of rows of keyframes that is shown in for example the timeline editor. Each row will be using a different thread. Currently the bottleneck is the summary channel that we could split up in the future even more ( although that is a complex refactoring work). Reviewed By: sybren Differential Revision: https://developer.blender.org/D12198 --- source/blender/editors/animation/keyframes_draw.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 8a884c0bd5b..d9a22c47242 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -30,6 +30,7 @@ #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" +#include "BLI_task.h" #include "DNA_anim_types.h" #include "DNA_gpencil_types.h" @@ -504,12 +505,25 @@ AnimKeylistDrawList *ED_keylist_draw_list_create(void) return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); } +static void ED_keylist_draw_list_elem_build_task(void *__restrict UNUSED(userdata), + void *item, + int UNUSED(index), + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + AnimKeylistDrawListElem *elem = item; + ED_keylist_draw_list_elem_build_keylist(elem); + ED_keylist_draw_list_elem_prepare_for_drawing(elem); +} + static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { - LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { - ED_keylist_draw_list_elem_build_keylist(elem); - ED_keylist_draw_list_elem_prepare_for_drawing(elem); - } + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + /* Create a thread per item, a single item is complex enough to deserve its own thread. */ + settings.min_iter_per_thread = 1; + + BLI_task_parallel_listbase( + &draw_list->channels, NULL, ED_keylist_draw_list_elem_build_task, &settings); } static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) -- cgit v1.2.3 From 7a5216497cc31e6d6e412a6d0aace8f17a51e585 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 10 Sep 2021 14:41:20 +0200 Subject: Fix possible crash displaying asset preview from current file For some reason the asset preview isn't created or loaded correctly in some instances. This could be addressed with D9974, but hard to tell since I only have a failing .blend file, no steps to recreate it from scratch. Would crash when opening an Asset Browser, selecting an object asset (that has an invalid preview stored) and opening the Asset Browser sidebar, so that the preview is visible there. --- source/blender/editors/space_file/filelist.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c7d23943b6c..46978972821 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1442,7 +1442,9 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat if (preview->in_memory_preview) { if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) { ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW); - preview->icon_id = BKE_icon_imbuf_create(imbuf); + if (imbuf) { + preview->icon_id = BKE_icon_imbuf_create(imbuf); + } done = true; } } -- cgit v1.2.3 From ca39aff59d45e15297a25895f6963e134894aad4 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 10 Sep 2021 14:43:48 +0200 Subject: Cleanup: Fix comment in recent commit. A task is created for each item in a list base. It used to say that a thread was created for each item. --- source/blender/editors/animation/keyframes_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index d9a22c47242..ac7db9f4f46 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -519,7 +519,7 @@ static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - /* Create a thread per item, a single item is complex enough to deserve its own thread. */ + /* Create a task per item, a single item is complex enough to deserve its own task. */ settings.min_iter_per_thread = 1; BLI_task_parallel_listbase( -- cgit v1.2.3 From 0467ff4053440982b70866a2868d8664c4ddc5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 10 Sep 2021 14:57:56 +0200 Subject: Python: extra check on `BPY_thread_save()` to ensure proper GIL handling Use `_PyThreadState_UncheckedGet()` to check that the current thread is tracked by Python before checking whether it has the GIL. The latter will abort when the former is false. --- source/blender/python/generic/bpy_threads.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/python/generic/bpy_threads.c b/source/blender/python/generic/bpy_threads.c index 54548b88d4f..8aa8c5c5d92 100644 --- a/source/blender/python/generic/bpy_threads.c +++ b/source/blender/python/generic/bpy_threads.c @@ -29,9 +29,12 @@ /* analogue of PyEval_SaveThread() */ BPy_ThreadStatePtr BPY_thread_save(void) { - /* Don't use `PyThreadState_Get()`, to avoid a fatal error issued when a thread state is NULL - * (the thread state can be NULL when quitting Blender). */ - if (PyGILState_Check()) { + /* Use `_PyThreadState_UncheckedGet()` instead of `PyThreadState_Get()`, to avoid a fatal error + * issued when a thread state is NULL (the thread state can be NULL when quitting Blender). + * + * `PyEval_SaveThread()` will release the GIL, so this thread has to have the GIL to begin with + * or badness will ensue. */ + if (_PyThreadState_UncheckedGet() && PyGILState_Check()) { return (BPy_ThreadStatePtr)PyEval_SaveThread(); } return NULL; -- cgit v1.2.3 From eb96f0cf06b9b4932979541fe4032328ad23f41f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 10 Sep 2021 15:00:09 +0200 Subject: Add missing bit to own previous commit Amendment to 7a5216497cc3. Removed this before committing, because I thought it wasn't needed. Of course it was... --- source/blender/editors/space_file/filelist.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 46978972821..511b5b255e9 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1955,7 +1955,9 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in if (entry->local_data.preview_image && BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) { ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW); - ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); + if (ibuf) { + ret->preview_icon_id = BKE_icon_imbuf_create(ibuf); + } } BLI_addtail(&cache->cached_entries, ret); return ret; -- cgit v1.2.3 From 42215d7cb8797ba5b631b9df93d07e895c4b1dda Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 10 Sep 2021 15:34:16 +0200 Subject: Tests: more graceful handling of keyboard interrupting benchmarks Leave current test result unchanged and stop executing immediately, so it can be continued. --- tests/performance/api/environment.py | 12 +++++------- tests/performance/api/graph.py | 2 +- tests/performance/benchmark | 21 ++++++++++++++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py index 750d991ebc8..eec92cc7b6b 100644 --- a/tests/performance/api/environment.py +++ b/tests/performance/api/environment.py @@ -98,6 +98,8 @@ class TestEnvironment: try: self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir) self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir) + except KeyboardInterrupt as e: + raise e except: return False @@ -193,17 +195,13 @@ class TestEnvironment: lines.append(line_str) if f: f.write(line_str) - except KeyboardInterrupt: + except KeyboardInterrupt as e: # Avoid processes that keep running when interrupting. proc.terminate() + raise e - if f: - f.close() - - # Print command output on error + # Raise error on failure if proc.returncode != 0 and not silent: - for line in lines: - print(line.rstrip()) raise Exception("Error executing command") return lines diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index fe4d4800894..e54adc194de 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -42,7 +42,7 @@ class TestGraph: # Generate one graph for every device x category x result key combination. for category, category_entries in categories.items(): - entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test, entry.date)) + entries = sorted(category_entries, key=lambda entry: (entry.date, entry.revision, entry.test)) outputs = set() for entry in entries: diff --git a/tests/performance/benchmark b/tests/performance/benchmark index 343af3be7d1..a58c339e9f8 100755 --- a/tests/performance/benchmark +++ b/tests/performance/benchmark @@ -141,6 +141,8 @@ def run_entry(env: api.TestEnvironment, if not entry.output: raise Exception("Test produced no output") entry.status = 'done' + except KeyboardInterrupt as e: + raise e except Exception as e: entry.status = 'failed' entry.error_msg = str(e) @@ -236,17 +238,26 @@ def cmd_run(env: api.TestEnvironment, argv: List, update_only: bool): configs = env.get_configs(args.config) for config in configs: updated = False + cancel = False print_header(config) for row in config.queue.rows(use_revision_columns(config)): if match_entry(row[0], args): for entry in row: - if run_entry(env, config, row, entry, update_only): - updated = True - # Write queue every time in case running gets interrupted, - # so it can be resumed. - config.queue.write() + try: + if run_entry(env, config, row, entry, update_only): + updated = True + # Write queue every time in case running gets interrupted, + # so it can be resumed. + config.queue.write() + except KeyboardInterrupt as e: + cancel = True + break + print_row(config, row) + if cancel: + break + if updated: # Generate graph if test were run. json_filepath = config.base_dir / "results.json" -- cgit v1.2.3 From 128eb6cbe928e58dfee1c64f340fd8d663134c26 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 9 Sep 2021 17:22:20 +0200 Subject: Modifiers: export motion blur velocity through attribute Previously fluid simulation and Alembic modifiers had a dedicated function to query the velocity for motion blur. Now use a more generic system where those modifiers output a velocity attribute. Advantages: * Geometry and particle nodes can output velocity through the same mechanism, or read the attribute coming from earlier modifiers. * The velocity can be preserved through modifiers like subdivision surface or auto smooth. * USD and Alembic previously only output velocity from fluid simulation, now they work with velocity from other sources too. * Simplifies the code for renderers like Cycles and exporters like Alembic and USD. This breaks compatibility: * External renderers and exporters accessing these velocities through the Python API now need to use the attribute instead. * Existing modifier node setups that create an attribute named "velocity" will render differently with motion blur. Differential Revision: https://developer.blender.org/D12305 --- intern/cycles/blender/blender_geometry.cpp | 4 +- intern/cycles/blender/blender_mesh.cpp | 181 ++++++++------------- intern/cycles/blender/blender_object.cpp | 12 +- intern/cycles/blender/blender_sync.h | 1 + intern/cycles/blender/blender_util.h | 28 +--- source/blender/blenkernel/BKE_attribute.h | 5 + source/blender/blenkernel/intern/attribute.c | 25 ++- source/blender/blenkernel/intern/fluid.c | 65 ++++---- source/blender/blenkernel/intern/modifier.c | 1 - source/blender/blenloader/intern/versioning_290.c | 2 - source/blender/io/alembic/ABC_alembic.h | 14 +- .../blender/io/alembic/exporter/abc_writer_mesh.cc | 56 ++----- .../blender/io/alembic/exporter/abc_writer_mesh.h | 5 +- source/blender/io/alembic/intern/abc_customdata.h | 6 + .../blender/io/alembic/intern/abc_reader_curves.cc | 2 + .../blender/io/alembic/intern/abc_reader_curves.h | 4 +- .../blender/io/alembic/intern/abc_reader_mesh.cc | 80 ++++++++- source/blender/io/alembic/intern/abc_reader_mesh.h | 8 +- .../blender/io/alembic/intern/abc_reader_object.cc | 2 + .../blender/io/alembic/intern/abc_reader_object.h | 10 +- source/blender/io/alembic/intern/alembic_capi.cc | 140 +--------------- source/blender/io/usd/intern/usd_writer_mesh.cc | 40 ++--- source/blender/io/usd/intern/usd_writer_mesh.h | 2 +- source/blender/makesdna/DNA_fluid_defaults.h | 2 - source/blender/makesdna/DNA_fluid_types.h | 9 +- source/blender/makesdna/DNA_modifier_defaults.h | 4 - source/blender/makesdna/DNA_modifier_types.h | 23 --- source/blender/makesrna/intern/rna_fluid.c | 24 --- source/blender/makesrna/intern/rna_modifier.c | 81 --------- .../modifiers/intern/MOD_meshsequencecache.c | 34 ++-- 30 files changed, 300 insertions(+), 570 deletions(-) diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/blender_geometry.cpp index acc089a286c..b1de37dac10 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/blender_geometry.cpp @@ -189,8 +189,10 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, /* Ensure we only sync instanced geometry once. */ Geometry *geom = object->get_geometry(); - if (geometry_motion_synced.find(geom) != geometry_motion_synced.end()) + if (geometry_motion_synced.find(geom) != geometry_motion_synced.end() || + geometry_motion_attribute_synced.find(geom) != geometry_motion_attribute_synced.end()) { return; + } geometry_motion_synced.insert(geom); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 9bb3447f56b..7ec430eb7fe 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -347,16 +347,57 @@ static void fill_generic_attribute(BL::Mesh &b_mesh, } } -static void attr_create_generic(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, bool subdivision) +static void attr_create_motion(Mesh *mesh, BL::Attribute &b_attribute, const float motion_scale) +{ + if (!(b_attribute.domain() == BL::Attribute::domain_POINT) && + (b_attribute.data_type() == BL::Attribute::data_type_FLOAT_VECTOR)) { + return; + } + + BL::FloatVectorAttribute b_vector_attribute(b_attribute); + const int numverts = mesh->get_verts().size(); + + /* Find or add attribute */ + float3 *P = &mesh->get_verts()[0]; + Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (!attr_mP) { + attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); + } + + /* Only export previous and next frame, we don't have any in between data. */ + float motion_times[2] = {-1.0f, 1.0f}; + for (int step = 0; step < 2; step++) { + const float relative_time = motion_times[step] * 0.5f * motion_scale; + float3 *mP = attr_mP->data_float3() + step * numverts; + + for (int i = 0; i < numverts; i++) { + mP[i] = P[i] + get_float3(b_vector_attribute.data[i].vector()) * relative_time; + } + } +} + +static void attr_create_generic(Scene *scene, + Mesh *mesh, + BL::Mesh &b_mesh, + const bool subdivision, + const bool need_motion, + const float motion_scale) { if (subdivision) { /* TODO: Handle subdivision correctly. */ return; } AttributeSet &attributes = mesh->attributes; + static const ustring u_velocity("velocity"); for (BL::Attribute &b_attribute : b_mesh.attributes) { const ustring name{b_attribute.name().c_str()}; + + if (need_motion && name == u_velocity) { + attr_create_motion(mesh, b_attribute, motion_scale); + } + if (!mesh->need_attribute(scene, name)) { continue; } @@ -859,8 +900,10 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh &b_mesh, const array &used_shaders, - bool subdivision = false, - bool subdivide_uvs = true) + const bool need_motion, + const float motion_scale, + const bool subdivision = false, + const bool subdivide_uvs = true) { /* count vertices and faces */ int numverts = b_mesh.vertices.length(); @@ -974,7 +1017,7 @@ static void create_mesh(Scene *scene, attr_create_vertex_color(scene, mesh, b_mesh, subdivision); attr_create_sculpt_vertex_color(scene, mesh, b_mesh, subdivision); attr_create_random_per_island(scene, mesh, b_mesh, subdivision); - attr_create_generic(scene, mesh, b_mesh, subdivision); + attr_create_generic(scene, mesh, b_mesh, subdivision, need_motion, motion_scale); if (subdivision) { attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs); @@ -1002,6 +1045,8 @@ static void create_subd_mesh(Scene *scene, BObjectInfo &b_ob_info, BL::Mesh &b_mesh, const array &used_shaders, + const bool need_motion, + const float motion_scale, float dicing_rate, int max_subdivisions) { @@ -1010,7 +1055,7 @@ static void create_subd_mesh(Scene *scene, BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length() - 1]); bool subdivide_uvs = subsurf_mod.uv_smooth() != BL::SubsurfModifier::uv_smooth_NONE; - create_mesh(scene, mesh, b_mesh, used_shaders, true, subdivide_uvs); + create_mesh(scene, mesh, b_mesh, used_shaders, need_motion, motion_scale, true, subdivide_uvs); /* export creases */ size_t num_creases = 0; @@ -1074,96 +1119,6 @@ static bool mesh_need_motion_attribute(BObjectInfo &b_ob_info, Scene *scene) return true; } -static void sync_mesh_cached_velocities(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh) -{ - if (!mesh_need_motion_attribute(b_ob_info, scene)) { - return; - } - - BL::Object b_ob = b_ob_info.real_object; - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, true, nullptr); - - if (!b_mesh_cache) { - return; - } - - if (!MeshSequenceCacheModifier_read_velocity_get(&b_mesh_cache.ptr)) { - return; - } - - const size_t numverts = mesh->get_verts().size(); - - if (b_mesh_cache.vertex_velocities.length() != numverts) { - return; - } - - /* Find or add attribute */ - float3 *P = &mesh->get_verts()[0]; - Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (!attr_mP) { - attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); - } - - /* Only export previous and next frame, we don't have any in between data. */ - float motion_times[2] = {-1.0f, 1.0f}; - for (int step = 0; step < 2; step++) { - const float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f; - float3 *mP = attr_mP->data_float3() + step * numverts; - - BL::MeshSequenceCacheModifier::vertex_velocities_iterator vvi; - int i = 0; - - for (b_mesh_cache.vertex_velocities.begin(vvi); vvi != b_mesh_cache.vertex_velocities.end(); - ++vvi, ++i) { - mP[i] = P[i] + get_float3(vvi->velocity()) * relative_time; - } - } -} - -static void sync_mesh_fluid_motion(BObjectInfo &b_ob_info, Scene *scene, Mesh *mesh) -{ - if (!b_ob_info.is_real_object_data()) { - return; - } - if (!mesh_need_motion_attribute(b_ob_info, scene)) { - return; - } - - BL::Object b_ob = b_ob_info.real_object; - BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob); - - if (!b_fluid_domain) - return; - - /* If the mesh has modifiers following the fluid domain we can't export motion. */ - if (b_fluid_domain.mesh_vertices.length() != mesh->get_verts().size()) - return; - - /* Find or add attribute */ - float3 *P = &mesh->get_verts()[0]; - Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (!attr_mP) { - attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); - } - - /* Only export previous and next frame, we don't have any in between data. */ - float motion_times[2] = {-1.0f, 1.0f}; - for (int step = 0; step < 2; step++) { - float relative_time = motion_times[step] * scene->motion_shutter_time() * 0.5f; - float3 *mP = attr_mP->data_float3() + step * mesh->get_verts().size(); - - BL::FluidDomainSettings::mesh_vertices_iterator svi; - int i = 0; - - for (b_fluid_domain.mesh_vertices.begin(svi); svi != b_fluid_domain.mesh_vertices.end(); - ++svi, ++i) { - mP[i] = P[i] + get_float3(svi->velocity()) * relative_time; - } - } -} - void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Mesh *mesh) { /* make a copy of the shaders as the caller in the main thread still need them for syncing the @@ -1187,6 +1142,13 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, M b_data, b_ob_info, b_depsgraph, need_undeformed, new_mesh.get_subdivision_type()); if (b_mesh) { + /* Motion blur attribute is relative to seconds, we need it relative to frames. */ + const bool need_motion = mesh_need_motion_attribute(b_ob_info, scene); + const float motion_scale = (need_motion) ? + scene->motion_shutter_time() / + (b_scene.render().fps() / b_scene.render().fps_base()) : + 0.0f; + /* Sync mesh itself. */ if (new_mesh.get_subdivision_type() != Mesh::SUBDIVISION_NONE) create_subd_mesh(scene, @@ -1194,21 +1156,23 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, M b_ob_info, b_mesh, new_mesh.get_used_shaders(), + need_motion, + motion_scale, dicing_rate, max_subdivisions); else - create_mesh(scene, &new_mesh, b_mesh, new_mesh.get_used_shaders(), false); + create_mesh(scene, + &new_mesh, + b_mesh, + new_mesh.get_used_shaders(), + need_motion, + motion_scale, + false); free_object_to_mesh(b_data, b_ob_info, b_mesh); } } - /* cached velocities (e.g. from alembic archive) */ - sync_mesh_cached_velocities(b_ob_info, scene, &new_mesh); - - /* mesh fluid motion mantaflow */ - sync_mesh_fluid_motion(b_ob_info, scene, &new_mesh); - /* update original sockets */ mesh->clear_non_sockets(); @@ -1242,19 +1206,6 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, Mesh *mesh, int motion_step) { - /* Fluid motion blur already exported. */ - BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob_info.real_object); - if (b_fluid_domain) { - return; - } - - /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find( - b_ob_info.real_object, true, nullptr); - if (mesh_cache) { - return; - } - /* Skip if no vertices were exported. */ size_t numverts = mesh->get_verts().size(); if (numverts == 0) { diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 2243baca0b2..22d6edeb099 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -604,7 +604,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, * only available in preview renders since currently do not have a good cache policy, the * data being loaded at once for all the frames. */ if (experimental && b_v3d) { - b_mesh_cache = object_mesh_cache_find(b_ob, false, &has_subdivision_modifier); + b_mesh_cache = object_mesh_cache_find(b_ob, &has_subdivision_modifier); use_procedural = b_mesh_cache && b_mesh_cache.cache_file().use_render_procedural(); } @@ -719,6 +719,14 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render, } } + /* Check which geometry already has motion blur so it can be skipped. */ + geometry_motion_attribute_synced.clear(); + for (Geometry *geom : scene->geometry) { + if (geom->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) { + geometry_motion_attribute_synced.insert(geom); + } + } + /* note iteration over motion_times set happens in sorted order */ foreach (float relative_time, motion_times) { /* center time is already handled. */ @@ -749,6 +757,8 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render, sync_objects(b_depsgraph, b_v3d, relative_time); } + geometry_motion_attribute_synced.clear(); + /* we need to set the python thread state again because this * function assumes it is being executed from python and will * try to save the thread state */ diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 76e8f23864c..d25c0ce1bc3 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -236,6 +236,7 @@ class BlenderSync { id_map particle_system_map; set geometry_synced; set geometry_motion_synced; + set geometry_motion_attribute_synced; set motion_times; void *world_map; bool world_recalc; diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index e69531ea707..04008d77d89 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -573,22 +573,6 @@ static inline bool object_use_deform_motion(BL::Object &b_parent, BL::Object &b_ return use_deform_motion; } -static inline BL::FluidDomainSettings object_fluid_liquid_domain_find(BL::Object &b_ob) -{ - for (BL::Modifier &b_mod : b_ob.modifiers) { - if (b_mod.is_a(&RNA_FluidModifier)) { - BL::FluidModifier b_mmd(b_mod); - - if (b_mmd.fluid_type() == BL::FluidModifier::fluid_type_DOMAIN && - b_mmd.domain_settings().domain_type() == BL::FluidDomainSettings::domain_type_LIQUID) { - return b_mmd.domain_settings(); - } - } - } - - return BL::FluidDomainSettings(PointerRNA_NULL); -} - static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b_ob) { for (BL::Modifier &b_mod : b_ob.modifiers) { @@ -606,7 +590,6 @@ static inline BL::FluidDomainSettings object_fluid_gas_domain_find(BL::Object &b } static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, - bool check_velocity, bool *has_subdivision_modifier) { for (int i = b_ob.modifiers.length() - 1; i >= 0; --i) { @@ -614,13 +597,6 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) { BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod); - - if (check_velocity) { - if (!MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { - return BL::MeshSequenceCacheModifier(PointerRNA_NULL); - } - } - return mesh_cache; } @@ -629,9 +605,7 @@ static inline BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b continue; } - /* Only skip the subsurf modifier if we are not checking for the mesh sequence cache modifier - * for motion blur. */ - if (b_mod.type() == BL::Modifier::type_SUBSURF && !check_velocity) { + if (b_mod.type() == BL::Modifier::type_SUBSURF) { if (has_subdivision_modifier) { *has_subdivision_modifier = true; } diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 5fda30224c6..7476474258b 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -66,6 +66,11 @@ bool BKE_id_attribute_remove(struct ID *id, struct CustomDataLayer *layer, struct ReportList *reports); +struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id, + const char *name, + const int type, + const AttributeDomain domain); + AttributeDomain BKE_id_attribute_domain(struct ID *id, struct CustomDataLayer *layer); int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer); bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer); diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index e9444cf88a6..ee8ef5e97f7 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -51,7 +51,7 @@ typedef struct DomainInfo { int length; } DomainInfo; -static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) +static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) { memset(info, 0, sizeof(DomainInfo) * ATTR_DOMAIN_NUM); @@ -223,6 +223,29 @@ bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports return true; } +CustomDataLayer *BKE_id_attribute_find(const ID *id, + const char *name, + const int type, + const AttributeDomain domain) +{ + DomainInfo info[ATTR_DOMAIN_NUM]; + get_domains(id, info); + + CustomData *customdata = info[domain].customdata; + if (customdata == NULL) { + return NULL; + } + + for (int i = 0; i < customdata->totlayer; i++) { + CustomDataLayer *layer = &customdata->layers[i]; + if (layer->type == type && STREQ(layer->name, name)) { + return layer; + } + } + + return NULL; +} + int BKE_id_attributes_length(ID *id, const CustomDataMask mask) { DomainInfo info[ATTR_DOMAIN_NUM]; diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 5a5e1208ff0..f13ed1f2bf7 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -39,6 +39,7 @@ #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" +#include "BKE_attribute.h" #include "BKE_effect.h" #include "BKE_fluid.h" #include "BKE_global.h" @@ -529,8 +530,7 @@ static bool BKE_fluid_modifier_init( copy_v3_v3_int(fds->res_max, res); /* Set time, frame length = 0.1 is at 25fps. */ - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + fds->frame_length = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; /* Initially dt is equal to frame length (dt can change with adaptive-time stepping though). */ fds->dt = fds->frame_length; fds->time_per_frame = 0; @@ -3256,7 +3256,10 @@ static void update_effectors( BKE_effectors_free(effectors); } -static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Object *ob) +static Mesh *create_liquid_geometry(FluidDomainSettings *fds, + Scene *scene, + Mesh *orgmesh, + Object *ob) { Mesh *me; MVert *mverts; @@ -3303,22 +3306,6 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj /* Normals are per vertex, so these must match. */ BLI_assert(num_verts == num_normals); - /* If needed, vertex velocities will be read too. */ - bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; - FluidDomainVertexVelocity *velarray = NULL; - float time_mult = 25.0f * DT_DEFAULT; - - if (use_speedvectors) { - if (fds->mesh_velocities) { - MEM_freeN(fds->mesh_velocities); - } - - fds->mesh_velocities = MEM_calloc_arrayN( - num_verts, sizeof(FluidDomainVertexVelocity), "fluid_mesh_vertvelocities"); - fds->totvert = num_verts; - velarray = fds->mesh_velocities; - } - me = BKE_mesh_new_nomain(num_verts, 0, 0, num_faces * 3, num_faces); if (!me) { return NULL; @@ -3350,6 +3337,18 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj /* Normals. */ normals = MEM_callocN(sizeof(short[3]) * num_normals, "Fluidmesh_tmp_normals"); + /* Velocities. */ + /* If needed, vertex velocities will be read too. */ + bool use_speedvectors = fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS; + float(*velarray)[3] = NULL; + float time_mult = fds->dx / (DT_DEFAULT * (25.0f / FPS)); + + if (use_speedvectors) { + CustomDataLayer *velocity_layer = BKE_id_attribute_new( + &me->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, NULL); + velarray = velocity_layer->data; + } + /* Loop for vertices and normals. */ for (i = 0, no_s = normals; i < num_verts && i < num_normals; i++, mverts++, no_s += 3) { @@ -3389,18 +3388,18 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj # endif if (use_speedvectors) { - velarray[i].vel[0] = manta_liquid_get_vertvel_x_at(fds->fluid, i) * (fds->dx / time_mult); - velarray[i].vel[1] = manta_liquid_get_vertvel_y_at(fds->fluid, i) * (fds->dx / time_mult); - velarray[i].vel[2] = manta_liquid_get_vertvel_z_at(fds->fluid, i) * (fds->dx / time_mult); + velarray[i][0] = manta_liquid_get_vertvel_x_at(fds->fluid, i) * time_mult; + velarray[i][1] = manta_liquid_get_vertvel_y_at(fds->fluid, i) * time_mult; + velarray[i][2] = manta_liquid_get_vertvel_z_at(fds->fluid, i) * time_mult; # ifdef DEBUG_PRINT /* Debugging: Print velocities of vertices. */ - printf("velarray[%d].vel[0]: %f, velarray[%d].vel[1]: %f, velarray[%d].vel[2]: %f\n", + printf("velarray[%d][0]: %f, velarray[%d][1]: %f, velarray[%d][2]: %f\n", i, - velarray[i].vel[0], + velarray[i][0], i, - velarray[i].vel[1], + velarray[i][1], i, - velarray[i].vel[2]); + velarray[i][2]); # endif } } @@ -3670,8 +3669,7 @@ static void manta_guiding( Depsgraph *depsgraph, Scene *scene, Object *ob, FluidModifierData *fmd, int frame) { FluidDomainSettings *fds = fmd->domain; - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - float dt = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + float dt = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; BLI_mutex_lock(&object_update_lock); @@ -3842,8 +3840,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, copy_v3_v3_int(o_shift, fds->shift); /* Ensure that time parameters are initialized correctly before every step. */ - float fps = scene->r.frs_sec / scene->r.frs_sec_base; - fds->frame_length = DT_DEFAULT * (25.0f / fps) * fds->time_scale; + fds->frame_length = DT_DEFAULT * (25.0f / FPS) * fds->time_scale; fds->dt = fds->frame_length; fds->time_per_frame = 0; @@ -4216,7 +4213,7 @@ struct Mesh *BKE_fluid_modifier_do( if (needs_viewport_update) { /* Return generated geometry depending on domain type. */ if (fmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) { - result = create_liquid_geometry(fmd->domain, me, ob); + result = create_liquid_geometry(fmd->domain, scene, me, ob); } if (fmd->domain->type == FLUID_DOMAIN_TYPE_GAS) { result = create_smoke_geometry(fmd->domain, me, ob); @@ -4773,8 +4770,6 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd) fmd->domain->point_cache[0] = NULL; } - MEM_SAFE_FREE(fmd->domain->mesh_velocities); - if (fmd->domain->coba) { MEM_freeN(fmd->domain->coba); } @@ -5010,16 +5005,12 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, tfds->viscosity_exponent = fds->viscosity_exponent; /* mesh options */ - if (fds->mesh_velocities) { - tfds->mesh_velocities = MEM_dupallocN(fds->mesh_velocities); - } tfds->mesh_concave_upper = fds->mesh_concave_upper; tfds->mesh_concave_lower = fds->mesh_concave_lower; tfds->mesh_particle_radius = fds->mesh_particle_radius; tfds->mesh_smoothen_pos = fds->mesh_smoothen_pos; tfds->mesh_smoothen_neg = fds->mesh_smoothen_neg; tfds->mesh_scale = fds->mesh_scale; - tfds->totvert = fds->totvert; tfds->mesh_generator = fds->mesh_generator; /* secondary particle options */ diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index b328a31cda5..b55b02c7bf2 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -1473,7 +1473,6 @@ void BKE_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb, Object fmd->domain->tex_velocity_y = NULL; fmd->domain->tex_velocity_z = NULL; fmd->domain->tex_wt = NULL; - fmd->domain->mesh_velocities = NULL; BLO_read_data_address(reader, &fmd->domain->coba); BLO_read_data_address(reader, &fmd->domain->effector_weights); diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index cb7a8ad592a..0e89c15ca1e 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1092,8 +1092,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (md->type == eModifierType_MeshSequenceCache) { MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; mcmd->velocity_scale = 1.0f; - mcmd->vertex_velocities = NULL; - mcmd->num_vertices = 0; } } } diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h index 0a3a43bb21f..0b5e927f02f 100644 --- a/source/blender/io/alembic/ABC_alembic.h +++ b/source/blender/io/alembic/ABC_alembic.h @@ -117,7 +117,9 @@ struct Mesh *ABC_read_mesh(struct CacheReader *reader, struct Mesh *existing_mesh, const float time, const char **err_str, - int read_flags); + const int read_flags, + const char *velocity_name, + const float velocity_scale); bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, @@ -133,16 +135,6 @@ struct CacheReader *CacheReader_open_alembic_object(struct CacheArchiveHandle *h struct Object *object, const char *object_path); -bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name); - -/* r_vertex_velocities should point to a preallocated array of num_vertices floats */ -int ABC_read_velocity_cache(struct CacheReader *reader, - const char *velocity_name, - float time, - float velocity_scale, - int num_vertices, - float *r_vertex_velocities); - #ifdef __cplusplus } #endif diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 131b60b90fb..8f410978211 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -25,6 +25,7 @@ #include "BLI_assert.h" #include "BLI_math_vector.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -108,9 +109,6 @@ void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *contex OBoolProperty type(typeContainer, "meshtype"); type.set(subsurf_modifier_ == nullptr); } - - Scene *scene_eval = DEG_get_evaluated_scene(args_.depsgraph); - liquid_sim_modifier_ = get_liquid_sim_modifier(scene_eval, context->object); } Alembic::Abc::OObject ABCGenericMeshWriter::get_alembic_object() const @@ -144,21 +142,6 @@ bool ABCGenericMeshWriter::export_as_subdivision_surface(Object *ob_eval) const return false; } -ModifierData *ABCGenericMeshWriter::get_liquid_sim_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = BKE_modifiers_findby_type(ob, eModifierType_Fluidsim); - - if (md && (BKE_modifier_is_enabled(scene, md, eModifierMode_Render))) { - FluidsimModifierData *fsmd = reinterpret_cast(md); - - if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { - return md; - } - } - - return nullptr; -} - bool ABCGenericMeshWriter::is_supported(const HierarchyContext *context) const { if (args_.export_params->visible_objects_only) { @@ -284,8 +267,7 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config); } - if (liquid_sim_modifier_ != nullptr) { - get_velocities(mesh, velocities); + if (get_velocities(mesh, velocities)) { mesh_sample.setVelocities(V3fArraySample(velocities)); } @@ -368,11 +350,6 @@ void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Sc void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) { - if (liquid_sim_modifier_ != nullptr) { - /* We don't need anything more for liquid meshes. */ - return; - } - if (frame_has_been_written_ || !args_.export_params->vcolors) { return; } @@ -387,27 +364,28 @@ void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me) write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL); } -void ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector &vels) +bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector &vels) { + /* Export velocity attribute output by fluid sim, sequence cache modifier + * and geometry nodes. */ + CustomDataLayer *velocity_layer = BKE_id_attribute_find( + &mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT); + + if (velocity_layer == nullptr) { + return false; + } + const int totverts = mesh->totvert; + const float(*mesh_velocities)[3] = reinterpret_cast(velocity_layer->data); vels.clear(); vels.resize(totverts); - FluidsimModifierData *fmd = reinterpret_cast(liquid_sim_modifier_); - FluidsimSettings *fss = fmd->fss; - - if (fss->meshVelocities) { - float *mesh_vels = reinterpret_cast(fss->meshVelocities); - - for (int i = 0; i < totverts; i++) { - copy_yup_from_zup(vels[i].getValue(), mesh_vels); - mesh_vels += 3; - } - } - else { - std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + for (int i = 0; i < totverts; i++) { + copy_yup_from_zup(vels[i].getValue(), mesh_velocities[i]); } + + return true; } void ABCGenericMeshWriter::get_geo_groups(Object *object, diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.h b/source/blender/io/alembic/exporter/abc_writer_mesh.h index 0e1792b9dab..fb8a01a3bbf 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.h +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.h @@ -45,7 +45,6 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { * exported object. */ bool is_subd_; ModifierData *subsurf_modifier_; - ModifierData *liquid_sim_modifier_; CDStreamConfig m_custom_data_config; @@ -70,10 +69,8 @@ class ABCGenericMeshWriter : public ABCAbstractWriter { void write_subd(HierarchyContext &context, Mesh *mesh); template void write_face_sets(Object *object, Mesh *mesh, Schema &schema); - ModifierData *get_liquid_sim_modifier(Scene *scene_eval, Object *ob_eval); - void write_arb_geo_params(Mesh *me); - void get_velocities(Mesh *mesh, std::vector &vels); + bool get_velocities(Mesh *mesh, std::vector &vels); void get_geo_groups(Object *object, Mesh *mesh, std::map> &geo_groups); diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index e9736555ead..4fba6a2f0f7 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -122,6 +122,12 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); +void read_velocity(const Alembic::Abc::ICompoundProperty &prop, + const Alembic::Abc::PropertyHeader *prop_header, + const Alembic::Abc::ISampleSelector &selector, + const CDStreamConfig &config, + const char *velocity_name, + const float velocity_scale); typedef enum { ABC_UV_SCOPE_NONE, ABC_UV_SCOPE_LOOP, diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 27ee35d1b39..688ba23333a 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -283,6 +283,8 @@ void AbcCurveReader::read_curve_sample(Curve *cu, Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, int /*read_flag*/, + const char * /*velocity_name*/, + const float /*velocity_scale*/, const char **err_str) { ICurvesSchema::Sample sample; diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index 075ed5ca6a1..11b23c8a8cf 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -45,7 +45,9 @@ class AbcCurveReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); void read_curve_sample(Curve *cu, diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index 77edd4908bd..dbccc8f2851 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_customdata_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -36,6 +37,7 @@ #include "BLI_listbase.h" #include "BLI_math_geom.h" +#include "BKE_attribute.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" @@ -43,8 +45,10 @@ #include "BKE_object.h" using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::IV3fArrayProperty; using Alembic::Abc::P3fArraySamplePtr; using Alembic::Abc::PropertyHeader; +using Alembic::Abc::V3fArraySamplePtr; using Alembic::AbcGeom::IC3fGeomParam; using Alembic::AbcGeom::IC4fGeomParam; @@ -420,6 +424,52 @@ static void get_weight_and_index(CDStreamConfig &config, config.ceil_index = i1; } +static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, + const ISampleSelector &selector, + const std::string &name) +{ + for (size_t i = 0; i < schema.getNumProperties(); i++) { + const PropertyHeader &header = schema.getPropertyHeader(i); + + if (header.isCompound()) { + const ICompoundProperty &prop = ICompoundProperty(schema, header.getName()); + + if (has_property(prop, name)) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); + if (velocity_prop) { + return velocity_prop.getValue(selector); + } + } + } + else if (header.isArray()) { + if (header.getName() == name) { + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); + return velocity_prop.getValue(selector); + } + } + } + + return V3fArraySamplePtr(); +} + +static void read_velocity(const V3fArraySamplePtr &velocities, + const CDStreamConfig &config, + const float velocity_scale) +{ + CustomDataLayer *velocity_layer = BKE_id_attribute_new( + &config.mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, NULL); + float(*velocity)[3] = (float(*)[3])velocity_layer->data; + + const int num_velocity_vectors = static_cast(velocities->size()); + BLI_assert(num_velocity_vectors == config.mesh->totvert); + + for (int i = 0; i < num_velocity_vectors; i++) { + const Imath::V3f &vel_in = (*velocities)[i]; + copy_zup_from_yup(velocity[i], vel_in.getValue()); + mul_v3_fl(velocity[i], velocity_scale); + } +} + static void read_mesh_sample(const std::string &iobject_full_name, ImportSettings *settings, const IPolyMeshSchema &schema, @@ -458,6 +508,13 @@ static void read_mesh_sample(const std::string &iobject_full_name, if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); } + + if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) { + V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name); + if (velocities) { + read_velocity(velocities, config, settings->velocity_scale); + } + } } CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation) @@ -563,7 +620,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { /* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ @@ -630,7 +687,9 @@ bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) { IPolyMeshSchema::Sample sample; @@ -673,6 +732,8 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, /* Only read point data when streaming meshes, unless we need to create new ones. */ ImportSettings settings; settings.read_flag |= read_flag; + settings.velocity_name = velocity_name; + settings.velocity_scale = velocity_scale; if (topology_changed(existing_mesh, sample_sel)) { new_mesh = BKE_mesh_new_nomain_from_template( @@ -829,6 +890,13 @@ static void read_subd_sample(const std::string &iobject_full_name, if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); } + + if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) { + V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name); + if (velocities) { + read_velocity(velocities, config, settings->velocity_scale); + } + } } /* ************************************************************************** */ @@ -876,7 +944,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); m_object->data = mesh; - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr); if (read_mesh != mesh) { BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true); } @@ -935,7 +1003,9 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, const ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) { ISubDSchema::Sample sample; @@ -962,6 +1032,8 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, ImportSettings settings; settings.read_flag |= read_flag; + settings.velocity_name = velocity_name; + settings.velocity_scale = velocity_scale; if (existing_mesh->totvert != positions->size()) { new_mesh = BKE_mesh_new_nomain_from_template( diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index 3329b723b77..d9f89cc8085 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -42,7 +42,9 @@ class AbcMeshReader : public AbcObjectReader { struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str) override; bool topology_changed(Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel) override; @@ -73,7 +75,9 @@ class AbcSubDReader : public AbcObjectReader { void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); }; diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc index 9a5ffd04bd1..a6d46c4b42a 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.cc +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -167,6 +167,8 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time) struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &UNUSED(sample_sel), int UNUSED(read_flag), + const char *UNUSED(velocity_name), + const float UNUSED(velocity_scale), const char **UNUSED(err_str)) { return existing_mesh; diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h index 89590b26b61..6e97f841a88 100644 --- a/source/blender/io/alembic/intern/abc_reader_object.h +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -50,6 +50,10 @@ struct ImportSettings { /* From MeshSeqCacheModifierData.read_flag */ int read_flag; + /* From CacheFile and MeshSeqCacheModifierData */ + std::string velocity_name; + float velocity_scale; + bool validate_meshes; bool always_add_cache_reader; @@ -65,6 +69,8 @@ struct ImportSettings { sequence_len(1), sequence_offset(0), read_flag(0), + velocity_name(""), + velocity_scale(1.0f), validate_meshes(false), always_add_cache_reader(false), cache_file(NULL) @@ -143,7 +149,9 @@ class AbcObjectReader { virtual struct Mesh *read_mesh(struct Mesh *mesh, const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, + const int read_flag, + const char *velocity_name, + const float velocity_scale, const char **err_str); virtual bool topology_changed(Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel); diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index deb945b767c..63565b902f3 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -786,7 +786,9 @@ Mesh *ABC_read_mesh(CacheReader *reader, Mesh *existing_mesh, const float time, const char **err_str, - int read_flag) + const int read_flag, + const char *velocity_name, + const float velocity_scale) { AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); if (abc_reader == nullptr) { @@ -794,7 +796,8 @@ Mesh *ABC_read_mesh(CacheReader *reader, } ISampleSelector sample_sel = sample_selector_for_time(time); - return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str); + return abc_reader->read_mesh( + existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str); } bool ABC_mesh_topology_changed( @@ -860,136 +863,3 @@ CacheReader *CacheReader_open_alembic_object(CacheArchiveHandle *handle, return reinterpret_cast(abc_reader); } - -/* ************************************************************************** */ - -static const PropertyHeader *get_property_header(const IPolyMeshSchema &schema, const char *name) -{ - const PropertyHeader *prop_header = schema.getPropertyHeader(name); - - if (prop_header) { - return prop_header; - } - - ICompoundProperty prop = schema.getArbGeomParams(); - - if (!has_property(prop, name)) { - return nullptr; - } - - return prop.getPropertyHeader(name); -} - -bool ABC_has_vec3_array_property_named(struct CacheReader *reader, const char *name) -{ - AbcObjectReader *abc_reader = reinterpret_cast(reader); - - if (!abc_reader) { - return false; - } - - IObject iobject = abc_reader->iobject(); - - if (!iobject.valid()) { - return false; - } - - const ObjectHeader &header = iobject.getHeader(); - - if (!IPolyMesh::matches(header)) { - return false; - } - - IPolyMesh mesh(iobject, kWrapExisting); - IPolyMeshSchema schema = mesh.getSchema(); - - const PropertyHeader *prop_header = get_property_header(schema, name); - - if (!prop_header) { - return false; - } - - return IV3fArrayProperty::matches(prop_header->getMetaData()); -} - -static V3fArraySamplePtr get_velocity_prop(const IPolyMeshSchema &schema, - const ISampleSelector &iss, - const std::string &name) -{ - const PropertyHeader *prop_header = schema.getPropertyHeader(name); - - if (prop_header) { - const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); - return velocity_prop.getValue(iss); - } - - ICompoundProperty prop = schema.getArbGeomParams(); - - if (!has_property(prop, name)) { - return V3fArraySamplePtr(); - } - - const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); - - if (velocity_prop) { - return velocity_prop.getValue(iss); - } - - return V3fArraySamplePtr(); -} - -int ABC_read_velocity_cache(CacheReader *reader, - const char *velocity_name, - const float time, - float velocity_scale, - int num_vertices, - float *r_vertex_velocities) -{ - AbcObjectReader *abc_reader = reinterpret_cast(reader); - - if (!abc_reader) { - return -1; - } - - IObject iobject = abc_reader->iobject(); - - if (!iobject.valid()) { - return -1; - } - - const ObjectHeader &header = iobject.getHeader(); - - if (!IPolyMesh::matches(header)) { - return -1; - } - - IPolyMesh mesh(iobject, kWrapExisting); - IPolyMeshSchema schema = mesh.getSchema(); - ISampleSelector sample_sel(time); - const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); - - V3fArraySamplePtr velocities = get_velocity_prop(schema, sample_sel, velocity_name); - - if (!velocities) { - return -1; - } - - float vel[3]; - - int num_velocity_vectors = static_cast(velocities->size()); - - if (num_velocity_vectors != num_vertices) { - return -1; - } - - for (size_t i = 0; i < velocities->size(); ++i) { - const Imath::V3f &vel_in = (*velocities)[i]; - copy_zup_from_yup(vel, vel_in.getValue()); - - mul_v3_fl(vel, velocity_scale); - - copy_v3_v3(r_vertex_velocities + i * 3, vel); - } - - return num_vertices; -} diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc index 54316e56867..61b14155dd0 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.cc +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -26,6 +26,7 @@ #include "BLI_assert.h" #include "BLI_math_vector.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_material.h" @@ -219,7 +220,7 @@ void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) if (usd_export_context_.export_params.export_normals) { write_normals(mesh, usd_mesh); } - write_surface_velocity(context.object, mesh, usd_mesh); + write_surface_velocity(mesh, usd_mesh); /* TODO(Sybren): figure out what happens when the face groups change. */ if (frame_has_been_written_) { @@ -409,42 +410,25 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_ usd_mesh.SetNormalsInterpolation(pxr::UsdGeomTokens->faceVarying); } -void USDGenericMeshWriter::write_surface_velocity(Object *object, - const Mesh *mesh, - pxr::UsdGeomMesh usd_mesh) +void USDGenericMeshWriter::write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) { - /* Only velocities from the fluid simulation are exported. This is the most important case, - * though, as the baked mesh changes topology all the time, and thus computing the velocities - * at import time in a post-processing step is hard. */ - ModifierData *md = BKE_modifiers_findby_type(object, eModifierType_Fluidsim); - if (md == nullptr) { - return; - } + /* Export velocity attribute output by fluid sim, sequence cache modifier + * and geometry nodes. */ + CustomDataLayer *velocity_layer = BKE_id_attribute_find( + &mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT); - /* Check that the fluid sim modifier is enabled and has useful data. */ - const bool use_render = (DEG_get_mode(usd_export_context_.depsgraph) == DAG_EVAL_RENDER); - const ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; - const Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); - if (!BKE_modifier_is_enabled(scene, md, required_mode)) { - return; - } - FluidsimModifierData *fsmd = reinterpret_cast(md); - if (!fsmd->fss || fsmd->fss->type != OB_FLUIDSIM_DOMAIN) { - return; - } - FluidsimSettings *fss = fsmd->fss; - if (!fss->meshVelocities) { + if (velocity_layer == nullptr) { return; } + const float(*velocities)[3] = reinterpret_cast(velocity_layer->data); + /* Export per-vertex velocity vectors. */ pxr::VtVec3fArray usd_velocities; usd_velocities.reserve(mesh->totvert); - FluidVertexVelocity *mesh_velocities = fss->meshVelocities; - for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; - ++vertex_idx, ++mesh_velocities) { - usd_velocities.push_back(pxr::GfVec3f(mesh_velocities->vel)); + for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; ++vertex_idx) { + usd_velocities.push_back(pxr::GfVec3f(velocities[vertex_idx])); } pxr::UsdTimeCode timecode = get_export_time_code(); diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h index 6345f2d4240..d60a6c4a59b 100644 --- a/source/blender/io/usd/intern/usd_writer_mesh.h +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -49,7 +49,7 @@ class USDGenericMeshWriter : public USDAbstractWriter { const MaterialFaceGroups &usd_face_groups); void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); - void write_surface_velocity(Object *object, const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + void write_surface_velocity(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); }; class USDMeshWriter : public USDGenericMeshWriter { diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h index 95f5b8b66b0..4135c4d40a8 100644 --- a/source/blender/makesdna/DNA_fluid_defaults.h +++ b/source/blender/makesdna/DNA_fluid_defaults.h @@ -50,7 +50,6 @@ .tex_flags = NULL, \ .tex_range_field = NULL, \ .guide_parent = NULL, \ - .mesh_velocities = NULL, \ .effector_weights = NULL, /* #BKE_effector_add_weights. */ \ .p0 = {0.0f, 0.0f, 0.0f}, \ .p1 = {0.0f, 0.0f, 0.0f}, \ @@ -122,7 +121,6 @@ .mesh_smoothen_pos = 1, \ .mesh_smoothen_neg = 1, \ .mesh_scale = 2, \ - .totvert = 0, \ .mesh_generator = FLUID_DOMAIN_MESH_IMPROVED, \ .particle_type = 0, \ .particle_scale = 1, \ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 835af3c6ff8..0cbef540306 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -480,10 +480,6 @@ enum { SM_HRES_FULLSAMPLE = 2, }; -typedef struct FluidDomainVertexVelocity { - float vel[3]; -} FluidDomainVertexVelocity; - typedef struct FluidDomainSettings { /* -- Runtime-only fields (from here on). -- */ @@ -509,8 +505,6 @@ typedef struct FluidDomainSettings { struct GPUTexture *tex_flags; struct GPUTexture *tex_range_field; struct Object *guide_parent; - /** Vertex velocities of simulated fluid mesh. */ - struct FluidDomainVertexVelocity *mesh_velocities; struct EffectorWeights *effector_weights; /* Domain object data. */ @@ -607,9 +601,8 @@ typedef struct FluidDomainSettings { int mesh_smoothen_pos; int mesh_smoothen_neg; int mesh_scale; - int totvert; short mesh_generator; - char _pad6[6]; /* Unused. */ + char _pad6[2]; /* Unused. */ /* Secondary particle options. */ int particle_type; diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 1b3dbd148df..5b2694f420b 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -419,10 +419,6 @@ .velocity_scale = 1.0f, \ .reader = NULL, \ .reader_object_path = "", \ - .vertex_velocities = NULL, \ - .num_vertices = 0, \ - .velocity_delta = 0.0f, \ - .last_lookup_time = 0.0f, \ } #define _DNA_DEFAULT_MirrorModifierData \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 8520786a030..31daa778b03 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2144,10 +2144,6 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; -typedef struct MeshCacheVertexVelocity { - float vel[3]; -} MeshCacheVertexVelocity; - typedef struct MeshSeqCacheModifierData { ModifierData modifier; @@ -2163,25 +2159,6 @@ typedef struct MeshSeqCacheModifierData { /* Runtime. */ struct CacheReader *reader; char reader_object_path[1024]; - - /* Vertex velocities read from the cache. The velocities are not automatically read during - * modifier execution, and therefore have to manually be read when needed. This is only used - * through the RNA for now. */ - struct MeshCacheVertexVelocity *vertex_velocities; - - /* The number of vertices of the Alembic mesh, set when the modifier is executed. */ - int num_vertices; - - /* Time (in frames or seconds) between two velocity samples. Automatically computed to - * scale the velocity vectors at render time for generating proper motion blur data. */ - float velocity_delta; - - /* Caches the scene time (in seconds) used to lookup data in the Alembic archive when the - * modifier was last executed. Used to access Alembic samples through the RNA. */ - float last_lookup_time; - - int _pad1; - void *_pad2; } MeshSeqCacheModifierData; /* MeshSeqCacheModifierData.read_flag */ diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 10e899b7ee3..90e77406f23 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1242,22 +1242,6 @@ static void rna_Fluid_flowtype_set(struct PointerRNA *ptr, int value) #else -static void rna_def_fluid_mesh_vertices(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "FluidDomainVertexVelocity", NULL); - RNA_def_struct_ui_text(srna, "Fluid Mesh Velocity", "Velocity of a simulated fluid mesh"); - RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); - - prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); - RNA_def_property_array(prop, 3); - RNA_def_property_float_sdna(prop, NULL, "vel"); - RNA_def_property_ui_text(prop, "Velocity", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); -} - static void rna_def_fluid_domain_settings(BlenderRNA *brna) { StructRNA *srna; @@ -2019,14 +2003,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mesh generator", "Which particle level set generator to use"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_update"); - prop = RNA_def_property(srna, "mesh_vertices", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "mesh_velocities", "totvert"); - RNA_def_property_struct_type(prop, "FluidDomainVertexVelocity"); - RNA_def_property_ui_text( - prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); - - rna_def_fluid_mesh_vertices(brna); - prop = RNA_def_property(srna, "use_mesh", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_MESH); RNA_def_property_ui_text(prop, "Use Mesh", "Enable fluid mesh (using amplification)"); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index fb4fd4528d6..c99dfea524f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1601,51 +1601,6 @@ static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) return md->ui_expand_flag & UI_PANEL_DATA_EXPAND_ROOT; } -static int rna_MeshSequenceCacheModifier_has_velocity_get(PointerRNA *ptr) -{ -# ifdef WITH_ALEMBIC - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; - return ABC_has_vec3_array_property_named(mcmd->reader, mcmd->cache_file->velocity_name); -# else - return false; - UNUSED_VARS(ptr); -# endif -} - -static int rna_MeshSequenceCacheModifier_read_velocity_get(PointerRNA *ptr) -{ -# ifdef WITH_ALEMBIC - MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)ptr->data; - - if (mcmd->num_vertices == 0) { - return 0; - } - - if (mcmd->vertex_velocities) { - MEM_freeN(mcmd->vertex_velocities); - } - - mcmd->vertex_velocities = MEM_mallocN(sizeof(MeshCacheVertexVelocity) * mcmd->num_vertices, - "Mesh Cache Velocities"); - - int num_read = ABC_read_velocity_cache(mcmd->reader, - mcmd->cache_file->velocity_name, - mcmd->last_lookup_time, - mcmd->velocity_scale * mcmd->velocity_delta, - mcmd->num_vertices, - (float *)mcmd->vertex_velocities); - - if (num_read == -1 || num_read != mcmd->num_vertices) { - return false; - } - - return true; -# else - return false; - UNUSED_VARS(ptr); -# endif -} - static bool rna_NodesModifier_node_group_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { bNodeTree *ntree = value.data; @@ -6118,22 +6073,6 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_define_lib_overridable(false); } -static void rna_def_mesh_cache_velocities(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, "MeshCacheVertexVelocity", NULL); - RNA_def_struct_ui_text(srna, "Mesh Cache Velocity", "Velocity attribute of an Alembic mesh"); - RNA_def_struct_ui_icon(srna, ICON_VERTEXSEL); - - prop = RNA_def_property(srna, "velocity", PROP_FLOAT, PROP_VELOCITY); - RNA_def_property_array(prop, 3); - RNA_def_property_float_sdna(prop, NULL, "vel"); - RNA_def_property_ui_text(prop, "Velocity", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); -} - static void rna_def_modifier_meshseqcache(BlenderRNA *brna) { StructRNA *srna; @@ -6190,26 +6129,6 @@ static void rna_def_modifier_meshseqcache(BlenderRNA *brna) "Multiplier used to control the magnitude of the velocity vectors for time effects"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); - /* -------------------------- Velocity Vectors -------------------------- */ - - prop = RNA_def_property(srna, "vertex_velocities", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "vertex_velocities", "num_vertices"); - RNA_def_property_struct_type(prop, "MeshCacheVertexVelocity"); - RNA_def_property_ui_text( - prop, "Fluid Mesh Vertices", "Vertices of the fluid mesh generated by simulation"); - - rna_def_mesh_cache_velocities(brna); - - prop = RNA_def_property(srna, "has_velocity", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_ui_text(prop, "Has Velocity Cache", ""); - RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_has_velocity_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - - prop = RNA_def_property(srna, "read_velocity", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_ui_text(prop, "Read Velocity Cache", ""); - RNA_def_property_boolean_funcs(prop, "rna_MeshSequenceCacheModifier_read_velocity_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_define_lib_overridable(false); } diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 259c1cb2417..bcaf294ec8b 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -105,10 +105,6 @@ static void freeData(ModifierData *md) mcmd->reader_object_path[0] = '\0'; BKE_cachefile_reader_free(mcmd->cache_file, &mcmd->reader); } - - if (mcmd->vertex_velocities) { - MEM_freeN(mcmd->vertex_velocities); - } } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -233,11 +229,26 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * Mesh *result = NULL; switch (cache_file->type) { - case CACHEFILE_TYPE_ALEMBIC: + case CACHEFILE_TYPE_ALEMBIC: { # ifdef WITH_ALEMBIC - result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag); + /* Time (in frames or seconds) between two velocity samples. Automatically computed to + * scale the velocity vectors at render time for generating proper motion blur data. */ + float velocity_scale = mcmd->velocity_scale; + if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) { + velocity_scale *= FPS; + } + + result = ABC_read_mesh(mcmd->reader, + ctx->object, + mesh, + time, + &err_str, + mcmd->read_flag, + mcmd->cache_file->velocity_name, + velocity_scale); # endif break; + } case CACHEFILE_TYPE_USD: # ifdef WITH_USD result = USD_read_mesh( @@ -248,17 +259,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * break; } - mcmd->velocity_delta = 1.0f; - if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) { - mcmd->velocity_delta /= FPS; - } - - mcmd->last_lookup_time = time; - - if (result != NULL) { - mcmd->num_vertices = result->totvert; - } - if (err_str) { BKE_modifier_set_error(ctx->object, md, "%s", err_str); } -- cgit v1.2.3 From e2f99c338bd5787658ce08984e6856b7d369a41f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 10:34:49 -0500 Subject: Geometry Nodes: Add versioning to change legacy node ID names Recently we have decided to avoid fancier versioning for nodes with string inputs for attribute names when updating the attribute workflow for 3.0. In that case we would just duplicate any node that will have an updated version to work with fields. We want to be able to use the "proper" ID names for the new versions of the nodes though, so this patch adds "Legacy" to the IDs of all nodes that will be replaced in 3.0. This commit also removes the nodes from the add menu when the fields experimental preference is enabled, in order to make it clear what has been updated and what hasn't. Nodes in the "Maybe" categories in versioning_300.c can be renamed later if necessary. For now it's probably better to be conservative, and to keep the list complete. Differential Revision: https://developer.blender.org/D12420 --- release/scripts/startup/nodeitems_builtins.py | 81 ++++++------ source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_node.h | 68 +++++----- source/blender/blenkernel/intern/node.cc | 9 +- source/blender/blenloader/intern/versioning_290.c | 15 ++- source/blender/blenloader/intern/versioning_300.c | 145 ++++++++++++++++++++- source/blender/editors/space_node/node_add.cc | 2 +- source/blender/makesrna/intern/rna_color.c | 4 +- source/blender/nodes/NOD_static_types.h | 70 +++++----- .../nodes/node_geo_align_rotation_to_vector.cc | 2 +- .../geometry/nodes/node_geo_attribute_clamp.cc | 3 +- .../nodes/node_geo_attribute_color_ramp.cc | 7 +- .../nodes/node_geo_attribute_combine_xyz.cc | 7 +- .../geometry/nodes/node_geo_attribute_compare.cc | 2 +- .../geometry/nodes/node_geo_attribute_convert.cc | 2 +- .../geometry/nodes/node_geo_attribute_curve_map.cc | 2 +- .../geometry/nodes/node_geo_attribute_fill.cc | 3 +- .../geometry/nodes/node_geo_attribute_map_range.cc | 2 +- .../geometry/nodes/node_geo_attribute_math.cc | 3 +- .../nodes/geometry/nodes/node_geo_attribute_mix.cc | 3 +- .../geometry/nodes/node_geo_attribute_proximity.cc | 2 +- .../geometry/nodes/node_geo_attribute_randomize.cc | 2 +- .../nodes/node_geo_attribute_sample_texture.cc | 2 +- .../nodes/node_geo_attribute_separate_xyz.cc | 2 +- .../geometry/nodes/node_geo_attribute_transfer.cc | 2 +- .../nodes/node_geo_attribute_vector_math.cc | 2 +- .../nodes/geometry/nodes/node_geo_curve_reverse.cc | 2 +- .../nodes/node_geo_curve_select_by_handle_type.cc | 2 +- .../geometry/nodes/node_geo_curve_set_handles.cc | 2 +- .../geometry/nodes/node_geo_curve_spline_type.cc | 2 +- .../geometry/nodes/node_geo_curve_subdivide.cc | 2 +- .../geometry/nodes/node_geo_delete_geometry.cc | 2 +- .../geometry/nodes/node_geo_material_assign.cc | 2 +- .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 3 +- .../geometry/nodes/node_geo_point_distribute.cc | 2 +- .../geometry/nodes/node_geo_point_instance.cc | 2 +- .../nodes/geometry/nodes/node_geo_point_rotate.cc | 2 +- .../nodes/geometry/nodes/node_geo_point_scale.cc | 2 +- .../geometry/nodes/node_geo_point_separate.cc | 2 +- .../geometry/nodes/node_geo_point_translate.cc | 2 +- .../geometry/nodes/node_geo_points_to_volume.cc | 2 +- .../nodes/geometry/nodes/node_geo_raycast.cc | 2 +- .../geometry/nodes/node_geo_select_by_material.cc | 2 +- 43 files changed, 321 insertions(+), 158 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 114e654e22b..c0af91b7ad6 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -183,6 +183,9 @@ def object_eevee_cycles_shader_nodes_poll(context): def geometry_nodes_fields_poll(context): return context.preferences.experimental.use_geometry_nodes_fields +def geometry_nodes_fields_legacy_poll(context): + return not context.preferences.experimental.use_geometry_nodes_fields + # All standard node categories currently used in nodes. @@ -480,25 +483,26 @@ texture_node_categories = [ geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeAttributeRandomize"), - NodeItem("GeometryNodeAttributeMath"), - NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeAttributeClamp"), - NodeItem("GeometryNodeAttributeCompare"), - NodeItem("GeometryNodeAttributeConvert"), - NodeItem("GeometryNodeAttributeCurveMap"), - NodeItem("GeometryNodeAttributeFill"), - NodeItem("GeometryNodeAttributeMix"), - NodeItem("GeometryNodeAttributeProximity"), - NodeItem("GeometryNodeAttributeColorRamp"), - NodeItem("GeometryNodeAttributeVectorMath"), - NodeItem("GeometryNodeAttributeVectorRotate"), - NodeItem("GeometryNodeAttributeSampleTexture"), - NodeItem("GeometryNodeAttributeCombineXYZ"), - NodeItem("GeometryNodeAttributeSeparateXYZ"), + NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeAttributeRemove"), - NodeItem("GeometryNodeAttributeMapRange"), - NodeItem("GeometryNodeAttributeTransfer"), + NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeMixRGB"), @@ -508,19 +512,20 @@ geometry_node_categories = [ NodeItem("ShaderNodeCombineRGB"), ]), GeometryNodeCategory("GEO_CURVE", "Curve", items=[ - NodeItem("GeometryNodeCurveSubdivide"), + NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeCurveToMesh"), NodeItem("GeometryNodeCurveResample"), - NodeItem("GeometryNodeMeshToCurve"), NodeItem("GeometryNodeCurveToPoints"), NodeItem("GeometryNodeCurveEndpoints"), NodeItem("GeometryNodeCurveFill"), NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), - NodeItem("GeometryNodeCurveReverse"), - NodeItem("GeometryNodeCurveSplineType"), - NodeItem("GeometryNodeCurveSetHandles"), - NodeItem("GeometryNodeCurveSelectHandles"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), @@ -532,13 +537,14 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurvePrimitiveBezierSegment"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ + NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeBoundBox"), NodeItem("GeometryNodeConvexHull"), - NodeItem("GeometryNodeDeleteGeometry"), NodeItem("GeometryNodeTransform"), NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), - NodeItem("GeometryNodeRaycast"), NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ @@ -555,8 +561,9 @@ geometry_node_categories = [ NodeItem("GeometryNodeInputNormal", poll=geometry_nodes_fields_poll), ]), GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ - NodeItem("GeometryNodeMaterialAssign"), - NodeItem("GeometryNodeSelectByMaterial"), + NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeMaterialReplace"), ]), GeometryNodeCategory("GEO_MESH", "Mesh", items=[ @@ -576,15 +583,14 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), ]), - GeometryNodeCategory("GEO_POINT", "Point", items=[ - NodeItem("GeometryNodePointDistribute"), - NodeItem("GeometryNodePointInstance"), - NodeItem("GeometryNodePointSeparate"), - NodeItem("GeometryNodePointScale"), - NodeItem("GeometryNodePointTranslate"), - NodeItem("GeometryNodeRotatePoints"), - NodeItem("GeometryNodeAlignRotationToVector"), + NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_fields_legacy_poll), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), @@ -606,7 +612,8 @@ geometry_node_categories = [ NodeItem("GeometryNodeViewer"), ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ - NodeItem("GeometryNodePointsToVolume"), + NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeVolumeToMesh"), ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e2788020628..d71cb559911 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 21 +#define BLENDER_FILE_SUBVERSION 22 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0d70e812ecb..9df3d7c544d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1412,34 +1412,34 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 #define GEO_NODE_BOOLEAN 1003 -#define GEO_NODE_POINT_DISTRIBUTE 1004 -#define GEO_NODE_POINT_INSTANCE 1005 +#define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 +#define GEO_NODE_LEGACY_POINT_INSTANCE 1005 #define GEO_NODE_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 -#define GEO_NODE_ATTRIBUTE_RANDOMIZE 1008 -#define GEO_NODE_ATTRIBUTE_MATH 1009 +#define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008 +#define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 -#define GEO_NODE_ATTRIBUTE_FILL 1011 -#define GEO_NODE_ATTRIBUTE_MIX 1012 -#define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 -#define GEO_NODE_POINT_SEPARATE 1014 -#define GEO_NODE_ATTRIBUTE_COMPARE 1015 -#define GEO_NODE_POINT_ROTATE 1016 -#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017 -#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018 -#define GEO_NODE_POINT_TRANSLATE 1019 -#define GEO_NODE_POINT_SCALE 1020 -#define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021 -#define GEO_NODE_POINTS_TO_VOLUME 1022 +#define GEO_NODE_LEGACY_ATTRIBUTE_FILL 1011 +#define GEO_NODE_LEGACY_ATTRIBUTE_MIX 1012 +#define GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP 1013 +#define GEO_NODE_LEGACY_POINT_SEPARATE 1014 +#define GEO_NODE_LEGACY_ATTRIBUTE_COMPARE 1015 +#define GEO_NODE_LEGACY_POINT_ROTATE 1016 +#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH 1017 +#define GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR 1018 +#define GEO_NODE_LEGACY_POINT_TRANSLATE 1019 +#define GEO_NODE_LEGACY_POINT_SCALE 1020 +#define GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE 1021 +#define GEO_NODE_LEGACY_POINTS_TO_VOLUME 1022 #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 -#define GEO_NODE_ATTRIBUTE_PROXIMITY 1025 +#define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 #define GEO_NODE_VOLUME_TO_MESH 1026 -#define GEO_NODE_ATTRIBUTE_COMBINE_XYZ 1027 -#define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028 +#define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 +#define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_MESH_SUBDIVIDE 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 -#define GEO_NODE_ATTRIBUTE_CONVERT 1031 +#define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031 #define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 #define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1033 #define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1034 @@ -1448,28 +1448,28 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_PRIMITIVE_CONE 1037 #define GEO_NODE_MESH_PRIMITIVE_LINE 1038 #define GEO_NODE_MESH_PRIMITIVE_GRID 1039 -#define GEO_NODE_ATTRIBUTE_MAP_RANGE 1040 -#define GEO_NODE_ATTRIBUTE_CLAMP 1041 +#define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040 +#define GEO_NODE_LECAGY_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 #define GEO_NODE_SWITCH 1043 -#define GEO_NODE_ATTRIBUTE_TRANSFER 1044 +#define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 -#define GEO_NODE_ATTRIBUTE_CURVE_MAP 1046 +#define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 #define GEO_NODE_CURVE_RESAMPLE 1047 #define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048 -#define GEO_NODE_MATERIAL_ASSIGN 1049 +#define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 #define GEO_NODE_MATERIAL_REPLACE 1051 -#define GEO_NODE_MESH_TO_CURVE 1052 -#define GEO_NODE_DELETE_GEOMETRY 1053 +#define GEO_NODE_LEGACY_MESH_TO_CURVE 1052 +#define GEO_NODE_LEGACY_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 -#define GEO_NODE_SELECT_BY_MATERIAL 1055 +#define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 #define GEO_NODE_CURVE_TO_POINTS 1057 -#define GEO_NODE_CURVE_REVERSE 1058 +#define GEO_NODE_LEGACY_CURVE_REVERSE 1058 #define GEO_NODE_SEPARATE_COMPONENTS 1059 -#define GEO_NODE_CURVE_SUBDIVIDE 1060 -#define GEO_NODE_RAYCAST 1061 +#define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060 +#define GEO_NODE_LEGACY_RAYCAST 1061 #define GEO_NODE_CURVE_PRIMITIVE_STAR 1062 #define GEO_NODE_CURVE_PRIMITIVE_SPIRAL 1063 #define GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER 1064 @@ -1480,9 +1480,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 #define GEO_NODE_CURVE_TRIM 1071 -#define GEO_NODE_CURVE_SET_HANDLES 1072 -#define GEO_NODE_CURVE_SPLINE_TYPE 1073 -#define GEO_NODE_CURVE_SELECT_HANDLES 1074 +#define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 +#define GEO_NODE_LEGACY_CURVE_SPLINE_TYPE 1073 +#define GEO_NODE_LEGACY_CURVE_SELECT_HANDLES 1074 #define GEO_NODE_CURVE_FILL 1075 #define GEO_NODE_INPUT_POSITION 1076 #define GEO_NODE_SET_POSITION 1077 diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 950026b9d65..e6af587cbd7 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -511,7 +511,8 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } - else if ((ntree->type == NTREE_GEOMETRY) && (node->type == GEO_NODE_ATTRIBUTE_CURVE_MAP)) { + else if ((ntree->type == NTREE_GEOMETRY) && + (node->type == GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP)) { BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; BKE_curvemapping_blend_write(writer, (const CurveMapping *)data->curve_vec); @@ -689,7 +690,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BKE_curvemapping_blend_read(reader, (CurveMapping *)node->storage); break; } - case GEO_NODE_ATTRIBUTE_CURVE_MAP: { + case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: { NodeAttributeCurveMap *data = (NodeAttributeCurveMap *)node->storage; BLO_read_data_address(reader, &data->curve_vec); if (data->curve_vec) { @@ -3888,7 +3889,7 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) } } if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) { + (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { tnode->flag &= ~NODE_ACTIVE_TEXTURE; } } @@ -3898,7 +3899,7 @@ void nodeSetActive(bNodeTree *ntree, bNode *node) node->flag |= NODE_ACTIVE_ID; } if ((node->typeinfo->nclass == NODE_CLASS_TEXTURE) || - (node->typeinfo->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE)) { + (node->typeinfo->type == GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE)) { node->flag |= NODE_ACTIVE_TEXTURE; } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 0e89c15ca1e..f023813555f 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1557,7 +1557,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_MATH && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH && node->storage == NULL) { const int old_use_attibute_a = (1 << 0); const int old_use_attibute_b = (1 << 1); NodeAttributeMath *data = MEM_callocN(sizeof(NodeAttributeMath), "NodeAttributeMath"); @@ -1718,7 +1718,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_POINT_INSTANCE && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_POINT_INSTANCE && node->storage == NULL) { NodeGeometryPointInstance *data = (NodeGeometryPointInstance *)MEM_callocN( sizeof(NodeGeometryPointInstance), __func__); data->instance_type = node->custom1; @@ -1735,7 +1735,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_MATH) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_MATH) { NodeAttributeMath *data = (NodeAttributeMath *)node->storage; data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; } @@ -1794,7 +1794,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_RANDOMIZE && node->storage == NULL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE && node->storage == NULL) { NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( sizeof(NodeAttributeRandomize), __func__); data->data_type = node->custom1; @@ -1830,7 +1830,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_ATTRIBUTE_PROXIMITY, "Result", "Distance"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Result", "Distance"); } } FOREACH_NODETREE_END; @@ -1839,7 +1839,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 10)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_ATTRIBUTE_PROXIMITY, "Location", "Position"); + version_node_socket_name( + ntree, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Location", "Position"); } } FOREACH_NODETREE_END; @@ -1933,7 +1934,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_ATTRIBUTE_FILL) { + if (node->type == GEO_NODE_LEGACY_ATTRIBUTE_FILL) { node->custom2 = ATTR_DOMAIN_AUTO; } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index f050de6e6e9..d0b96957af5 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -446,7 +446,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) continue; } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { + if (node->type != GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE) { continue; } if (node->id == NULL) { @@ -630,6 +630,141 @@ static bNodeSocket *do_version_replace_float_size_with_vector(bNodeTree *ntree, return new_socket; } +static bool geometry_node_is_293_legacy(const short node_type) +{ + switch (node_type) { + /* Not legacy: No attribute inputs or outputs. */ + case GEO_NODE_TRIANGULATE: + case GEO_NODE_EDGE_SPLIT: + case GEO_NODE_TRANSFORM: + case GEO_NODE_BOOLEAN: + case GEO_NODE_SUBDIVISION_SURFACE: + case GEO_NODE_IS_VIEWPORT: + case GEO_NODE_MESH_SUBDIVIDE: + case GEO_NODE_MESH_PRIMITIVE_CUBE: + case GEO_NODE_MESH_PRIMITIVE_CIRCLE: + case GEO_NODE_MESH_PRIMITIVE_UV_SPHERE: + case GEO_NODE_MESH_PRIMITIVE_CYLINDER: + case GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE: + case GEO_NODE_MESH_PRIMITIVE_CONE: + case GEO_NODE_MESH_PRIMITIVE_LINE: + case GEO_NODE_MESH_PRIMITIVE_GRID: + case GEO_NODE_BOUNDING_BOX: + case GEO_NODE_CURVE_RESAMPLE: + case GEO_NODE_INPUT_MATERIAL: + case GEO_NODE_MATERIAL_REPLACE: + case GEO_NODE_CURVE_LENGTH: + case GEO_NODE_CONVEX_HULL: + case GEO_NODE_SEPARATE_COMPONENTS: + case GEO_NODE_CURVE_PRIMITIVE_STAR: + case GEO_NODE_CURVE_PRIMITIVE_SPIRAL: + case GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER: + case GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT: + case GEO_NODE_CURVE_PRIMITIVE_CIRCLE: + case GEO_NODE_VIEWER: + case GEO_NODE_CURVE_PRIMITIVE_LINE: + case GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL: + case GEO_NODE_CURVE_FILL: + case GEO_NODE_CURVE_TRIM: + case GEO_NODE_CURVE_TO_MESH: + return false; + + /* Not legacy: Newly added with fields patch. */ + case GEO_NODE_INPUT_POSITION: + case GEO_NODE_SET_POSITION: + case GEO_NODE_INPUT_INDEX: + case GEO_NODE_INPUT_NORMAL: + case GEO_NODE_ATTRIBUTE_CAPTURE: + return false; + + /* Maybe legacy: Might need special attribute handling, depending on design. */ + case GEO_NODE_SWITCH: + case GEO_NODE_JOIN_GEOMETRY: + case GEO_NODE_ATTRIBUTE_REMOVE: + case GEO_NODE_OBJECT_INFO: + case GEO_NODE_COLLECTION_INFO: + return false; + + /* Maybe legacy: Transfered *all* attributes before, will not transfer all built-ins now. */ + case GEO_NODE_CURVE_ENDPOINTS: + case GEO_NODE_CURVE_TO_POINTS: + return false; + + /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to generate + * a mesh for all grids in the volume. */ + case GEO_NODE_VOLUME_TO_MESH: + return false; + + /* Legacy: Attribute operation completely replaced by field nodes. */ + case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE: + case GEO_NODE_LEGACY_ATTRIBUTE_MATH: + case GEO_NODE_LEGACY_ATTRIBUTE_FILL: + case GEO_NODE_LEGACY_ATTRIBUTE_MIX: + case GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_COMPARE: + case GEO_NODE_LEGACY_POINT_ROTATE: + case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR: + case GEO_NODE_LEGACY_POINT_SCALE: + case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE: + case GEO_NODE_ATTRIBUTE_VECTOR_ROTATE: + case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: + case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE: + case GEO_NODE_LECAGY_ATTRIBUTE_CLAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH: + case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ: + case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ: + return true; + + /* Legacy: Replaced by field node depending on another geometry. */ + case GEO_NODE_LEGACY_RAYCAST: + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER: + case GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY: + return true; + + /* Legacy: Simple selection attribute input. */ + case GEO_NODE_LEGACY_MESH_TO_CURVE: + case GEO_NODE_LEGACY_POINT_SEPARATE: + case GEO_NODE_LEGACY_CURVE_SELECT_HANDLES: + case GEO_NODE_LEGACY_CURVE_SPLINE_TYPE: + case GEO_NODE_LEGACY_CURVE_REVERSE: + case GEO_NODE_LEGACY_MATERIAL_ASSIGN: + case GEO_NODE_LEGACY_CURVE_SET_HANDLES: + return true; + + /* Legacy: More complex attribute inputs or outputs. */ + case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain dropdown. */ + case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ + case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ + case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ + case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ + case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ + return true; + } + return false; +} + +static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (geometry_node_is_293_legacy(node->type)) { + if (strstr(node->idname, "Legacy")) { + /* Make sure we haven't changed this idname already, better safe than sorry. */ + continue; + } + + char temp_idname[sizeof(node->idname)]; + BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); + + BLI_snprintf(node->idname, + sizeof(node->idname), + "GeometryNodeLegacy%s", + temp_idname + strlen("GeometryNode")); + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -1084,6 +1219,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 21)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_change_legacy_names(ntree); + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 9264c9d3572..4d2e00e97a1 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -575,7 +575,7 @@ static int node_add_texture_exec(bContext *C, wmOperator *op) bNode *texture_node = node_add_node(C, nullptr, - GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, snode->runtime->cursor[0], snode->runtime->cursor[1]); if (!texture_node) { diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index 1ac6dd021e9..5c12fc3a227 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -189,7 +189,7 @@ static char *rna_ColorRamp_path(PointerRNA *ptr) SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, - GEO_NODE_ATTRIBUTE_COLOR_RAMP)) { + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { if (node->storage == ptr->data) { /* all node color ramp properties called 'color_ramp' * prepend path from ID to the node @@ -320,7 +320,7 @@ static void rna_ColorRamp_update(Main *bmain, Scene *UNUSED(scene), PointerRNA * SH_NODE_VALTORGB, CMP_NODE_VALTORGB, TEX_NODE_VALTORGB, - GEO_NODE_ATTRIBUTE_COLOR_RAMP)) { + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP)) { ED_node_tag_update_nodetree(bmain, ntree, node); } } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 670cde4470e..a6c4ab342be 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -269,26 +269,26 @@ DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") -DefNode(GeometryNode, GEO_NODE_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "ALIGN_ROTATION_TO_VECTOR", AlignRotationToVector, "Align Rotation to Vector", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "ATTRIBUTE_CLAMP", AttributeClamp, "Attribute Clamp", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") +DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "LEGACY_ATTRIBUTE_CONVERT", LegacyAttributeConvert, "Attribute Convert", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "LEGACY_ATTRIBUTE_CURVE_MAP", LegacyAttributeCurveMap, "Attribute Curve Map", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "ATTRIBUTE_PROXIMITY", AttributeProximity, "Attribute Proximity", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "ATTRIBUTE_RANDOMIZE", AttributeRandomize, "Attribute Randomize", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "LEGACY_ATTRIBUTE_FILL", LegacyAttributeFill, "Attribute Fill", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, 0, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Attribute Transfer", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") @@ -304,15 +304,15 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") -DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -320,7 +320,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "No DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") @@ -331,17 +331,17 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") -DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") -DefNode(GeometryNode, GEO_NODE_POINT_DISTRIBUTE, def_geo_point_distribute, "POINT_DISTRIBUTE", PointDistribute, "Point Distribute", "") -DefNode(GeometryNode, GEO_NODE_POINT_INSTANCE, def_geo_point_instance, "POINT_INSTANCE", PointInstance, "Point Instance", "") -DefNode(GeometryNode, GEO_NODE_POINT_ROTATE, def_geo_point_rotate, "POINT_ROTATE", RotatePoints, "Point Rotate", "") -DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", PointScale, "Point Scale", "") -DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") -DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") -DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_INSTANCE, def_geo_point_instance, "LEGACY_POINT_INSTANCE", LegacyPointInstance, "Point Instance", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGACY_POINT_ROTATE", LegacyRotatePoints, "Point Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc index b76556b6c6c..d0bb906e8af 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc @@ -226,7 +226,7 @@ void register_node_type_geo_align_rotation_to_vector() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ALIGN_ROTATION_TO_VECTOR, + GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, "Align Rotation to Vector", NODE_CLASS_GEOMETRY, 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc index 3211cdbc5a7..97070184609 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc @@ -268,7 +268,8 @@ void register_node_type_geo_attribute_clamp() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base( + &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update); ntype.declare = blender::nodes::geo_node_attribute_clamp_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index aae906f2e5e..aa054af3acd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -125,8 +125,11 @@ void register_node_type_geo_attribute_color_ramp() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COLOR_RAMP, "Attribute Color Ramp", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, + "Attribute Color Ramp", + NODE_CLASS_ATTRIBUTE, + 0); node_type_storage( &ntype, "NodeAttributeColorRamp", node_free_standard_storage, node_copy_standard_storage); node_type_init(&ntype, blender::nodes::geo_node_attribute_color_ramp_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc index 1638793525a..569d5a824ca 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -136,8 +136,11 @@ void register_node_type_geo_attribute_combine_xyz() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, "Attribute Combine XYZ", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, + "Attribute Combine XYZ", + NODE_CLASS_ATTRIBUTE, + 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_combine_xyz_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_combine_xyz_update); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index 75c88a6953a..0b9708dae14 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -348,7 +348,7 @@ void register_node_type_geo_attribute_compare() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); ntype.declare = blender::nodes::geo_node_attribute_compare_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_compare_exec; ntype.draw_buttons = blender::nodes::geo_node_attribute_compare_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc index 72e1930952a..a2382aa9d25 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -182,7 +182,7 @@ void register_node_type_geo_attribute_convert() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); ntype.declare = blender::nodes::geo_node_attribute_convert_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec; ntype.draw_buttons = blender::nodes::geo_node_attribute_convert_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc index cb86dfa90a8..b9621b4ae92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc @@ -211,7 +211,7 @@ void register_node_type_geo_attribute_curve_map() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, "Attribute Curve Map", NODE_CLASS_ATTRIBUTE, 0); node_type_update(&ntype, blender::nodes::geo_node_attribute_curve_map_update); node_type_init(&ntype, blender::nodes::geo_node_attribute_curve_map_init); node_type_size_preset(&ntype, NODE_SIZE_LARGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index 632e4f86572..3c50ae5c837 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -152,7 +152,8 @@ void register_node_type_geo_attribute_fill() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_FILL, "Attribute Fill", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_fill_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_fill_update); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_fill_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc index 4cb452a97cd..0ea3bbe1e45 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc @@ -420,7 +420,7 @@ void register_node_type_geo_attribute_map_range() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, "Attribute Map Range", NODE_CLASS_ATTRIBUTE, 0); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_map_range_exec; node_type_init(&ntype, blender::nodes::geo_node_attribute_map_range_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_map_range_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index de1d5ca815d..efa09215b45 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -301,7 +301,8 @@ void register_node_type_geo_attribute_math() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MATH, "Attribute Math", NODE_CLASS_ATTRIBUTE, 0); ntype.declare = blender::nodes::geo_node_attribute_math_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec; ntype.draw_buttons = blender::nodes::geo_node_attribute_math_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 5d3d75b4678..74e05cb997d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -243,7 +243,8 @@ static void geo_node_attribute_mix_exec(GeoNodeExecParams params) void register_node_type_geo_attribute_mix() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_MIX, "Attribute Mix", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_mix_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_mix_update); ntype.declare = blender::nodes::geo_node_mix_attribute_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index 9ca19f70be6..0cf411343cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -237,7 +237,7 @@ void register_node_type_geo_attribute_proximity() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, "Attribute Proximity", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_attribute_proximity_init); node_type_storage(&ntype, "NodeGeometryAttributeProximity", diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index 64035c4fc16..60b9910399c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -331,7 +331,7 @@ void register_node_type_geo_attribute_randomize() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index c78a3d956e3..c4fd0ca4008 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -126,7 +126,7 @@ void register_node_type_geo_sample_texture() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, + GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, "Attribute Sample Texture", NODE_CLASS_ATTRIBUTE, 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc index b1ac608faad..232e0376a11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc @@ -158,7 +158,7 @@ void register_node_type_geo_attribute_separate_xyz() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, "Attribute Separate XYZ", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, "Attribute Separate XYZ", NODE_CLASS_ATTRIBUTE, 0); ntype.declare = blender::nodes::geo_node_attribute_separate_xyz_declare; node_type_init(&ntype, blender::nodes::geo_node_attribute_separate_xyz_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_separate_xyz_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index 6cef09aca67..874350cd714 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -516,7 +516,7 @@ void register_node_type_geo_attribute_transfer() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, "Attribute Transfer", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_transfer_init); node_type_storage(&ntype, "NodeGeometryAttributeTransfer", diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 47f2cf39d51..72714cff6b7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -556,7 +556,7 @@ void register_node_type_geo_attribute_vector_math() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0); ntype.declare = blender::nodes::geo_node_attribute_vector_math_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec; ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_math_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 720af50b795..3312733d2fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -121,7 +121,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc index bf33cf0294e..c5373d8f9ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc @@ -128,7 +128,7 @@ void register_node_type_geo_select_by_handle_type() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_select_by_handle_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_select_by_handle_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 0b5be7addaf..31c13134f79 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -130,7 +130,7 @@ void register_node_type_geo_curve_set_handles() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre; ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 6380873fbcb..0ef107fd8a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -288,7 +288,7 @@ void register_node_type_geo_curve_spline_type() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 2809543a73d..9e116770df5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -377,7 +377,7 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; node_type_storage(&ntype, diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index b36b12d8cf5..0142a81ba92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -668,7 +668,7 @@ void register_node_type_geo_delete_geometry() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_delete_geometry_declare; ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 4d0b4cfecbc..60a9a8ced33 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -87,7 +87,7 @@ void register_node_type_geo_material_assign() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_assign_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 298c6fc1ffe..11349dc7d42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -306,7 +306,8 @@ void register_node_type_geo_mesh_to_curve() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc index dcab1ea3fb2..04b4003daed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc @@ -649,7 +649,7 @@ void register_node_type_geo_point_distribute() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_POINT_DISTRIBUTE, "Point Distribute", NODE_CLASS_GEOMETRY, 0); node_type_update(&ntype, blender::nodes::node_point_distribute_update); ntype.declare = blender::nodes::geo_node_point_distribute_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 72e9e8f5c29..987aee7c270 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -259,7 +259,7 @@ void register_node_type_geo_point_instance() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_instance_init); node_type_storage( &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc index 4d77bcc132f..60c82360007 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc @@ -219,7 +219,7 @@ void register_node_type_geo_point_rotate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_ROTATE, "Point Rotate", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_rotate_init); node_type_update(&ntype, blender::nodes::geo_node_point_rotate_update); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc index 1a7ab9817d9..99adce149e9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc @@ -126,7 +126,7 @@ void register_node_type_geo_point_scale() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SCALE, "Point Scale", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_point_scale_declare; node_type_init(&ntype, blender::nodes::geo_node_point_scale_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 8752c1c669e..7849b59b6b1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -164,7 +164,7 @@ void register_node_type_geo_point_separate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_point_instance_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; ntype.geometry_node_execute_supports_laziness = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index d187bf0fa71..ec78223ad0b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -95,7 +95,7 @@ void register_node_type_geo_point_translate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_translate_init); node_type_update(&ntype, blender::nodes::geo_node_point_translate_update); node_type_storage(&ntype, diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 8f34fff9f66..d920c8de9f0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -263,7 +263,7 @@ void register_node_type_geo_points_to_volume() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); node_type_storage(&ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index ed7ed87fa46..401a478f04c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -308,7 +308,7 @@ void register_node_type_geo_raycast() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_init(&ntype, blender::nodes::geo_node_raycast_init); node_type_update(&ntype, blender::nodes::geo_node_raycast_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc index 79b93502103..40c990346e5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc @@ -85,7 +85,7 @@ void register_node_type_geo_select_by_material() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_select_by_material_declare; ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec; nodeRegisterType(&ntype); -- cgit v1.2.3 From 6b7b4f8e573afa183c8b7d2f41521bc6ab9e5f36 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 10 Sep 2021 09:30:21 -0700 Subject: VFont: Refactor of check_freetypefont() Refactor of our Vfont check for font validity. See D12068 for further details. Differential Revision: https://developer.blender.org/D12068 Reviewed by Campbell Barton --- source/blender/blenlib/intern/freetypefont.c | 37 ++++++++++------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index e1e3aa273b5..34de8fe7f6d 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -369,36 +369,28 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) return vfd; } -static int check_freetypefont(PackedFile *pf) +static bool check_freetypefont(PackedFile *pf) { - FT_Face face; - FT_GlyphSlot glyph; - FT_UInt glyph_index; - int success = 0; + FT_Face face = NULL; + FT_UInt glyph_index = 0; + bool success = false; err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face); if (err) { - success = 0; + return false; // XXX error("This is not a valid font"); } - else { - glyph_index = FT_Get_Char_Index(face, 'A'); + + FT_Get_First_Char(face, &glyph_index); + if (glyph_index) { err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); - if (err) { - success = 0; - } - else { - glyph = face->glyph; - if (glyph->format == ft_glyph_format_outline) { - success = 1; - } - else { - // XXX error("Selected Font has no outline data"); - success = 0; - } + if (!err) { + success = (face->glyph->format == ft_glyph_format_outline); } } + FT_Done_Face(face); + return success; } @@ -413,7 +405,6 @@ static int check_freetypefont(PackedFile *pf) VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) { VFontData *vfd = NULL; - int success = 0; /* init Freetype */ err = FT_Init_FreeType(&library); @@ -422,9 +413,7 @@ VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) return NULL; } - success = check_freetypefont(pf); - - if (success) { + if (check_freetypefont(pf)) { vfd = objfnt_to_ftvfontdata(pf); } -- cgit v1.2.3 From 28f773925af01c1d9d23e58c7cce49efdc0b65a7 Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Fri, 10 Sep 2021 18:48:41 +0200 Subject: Fix Constraints not updating on move in stack Flag the changed object and its bones to update after moving a constraint in the stack up or down. The two operators for move up and move down seem to be unused, but I notices they had the same problem, so I added the update there as well. Reviewed By: sybren Differential Revision: https://developer.blender.org/D12174 --- source/blender/editors/object/object_constraint.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index e0419e0a4cc..c46e868899c 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1862,6 +1862,7 @@ static int constraint_move_down_exec(bContext *C, wmOperator *op) BLI_remlink(conlist, con); BLI_insertlinkafter(conlist, nextCon, con); + ED_object_constraint_update(CTX_data_main(C), ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); return OPERATOR_FINISHED; @@ -1917,6 +1918,7 @@ static int constraint_move_up_exec(bContext *C, wmOperator *op) BLI_remlink(conlist, con); BLI_insertlinkbefore(conlist, prevCon, con); + ED_object_constraint_update(CTX_data_main(C), ob); WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob); return OPERATOR_FINISHED; @@ -1970,6 +1972,8 @@ static int constraint_move_to_index_exec(bContext *C, wmOperator *op) if (con) { ED_object_constraint_move_to_index(ob, con, new_index); + ED_object_constraint_update(CTX_data_main(C), ob); + return OPERATOR_FINISHED; } -- cgit v1.2.3 From 5e0684b07d2504076b220f2eba2bda27ffe9d084 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 10 Sep 2021 18:30:04 +0200 Subject: Revert startup.blend changes from commit a00507c482e2 It appears an old version of this file was committed by accident. The intended change to remove sculpt layers was done in versioning code and does not require a modified startup.blend. --- release/datafiles/startup.blend | Bin 650648 -> 804804 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend index 8caec075138..20ebf5d9986 100644 Binary files a/release/datafiles/startup.blend and b/release/datafiles/startup.blend differ -- cgit v1.2.3 From 6a00e7a428da4dfdfee3632a665fe7f61e8e0ae1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 13:26:46 -0500 Subject: Fix: Incorrect file subversion for geometry node versioning rBe2f99c338bd57 should have used the subversion currently defined in Blender, instead it used one lower, so some files didn't have the versioning applied. --- source/blender/blenloader/intern/versioning_300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index d0b96957af5..b474209e618 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1219,7 +1219,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 300, 21)) { + if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) { LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { if (ntree->type == NTREE_GEOMETRY) { version_geometry_nodes_change_legacy_names(ntree); -- cgit v1.2.3 From 8745bb9628a211f9f37c62b8305c3c543965b2d5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 15:58:18 -0500 Subject: Cleanup: Split legacy node type defines --- source/blender/nodes/NOD_static_types.h | 41 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index a6c4ab342be..215dca63fa3 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -269,25 +269,43 @@ DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "LEGACY_ATTRIBUTE_CONVERT", LegacyAttributeConvert, "Attribute Convert", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "LEGACY_ATTRIBUTE_CURVE_MAP", LegacyAttributeCurveMap, "Attribute Curve Map", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "LEGACY_ATTRIBUTE_FILL", LegacyAttributeFill, "Attribute Fill", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_INSTANCE, def_geo_point_instance, "LEGACY_POINT_INSTANCE", LegacyPointInstance, "Point Instance", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGACY_POINT_ROTATE", LegacyRotatePoints, "Point Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") + +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") @@ -304,15 +322,9 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_prim DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") @@ -320,7 +332,6 @@ DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "No DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") @@ -331,17 +342,7 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_INSTANCE, def_geo_point_instance, "LEGACY_POINT_INSTANCE", LegacyPointInstance, "Point Instance", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGACY_POINT_ROTATE", LegacyRotatePoints, "Point Rotate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") -- cgit v1.2.3 From eab26f13347dca6f14cf961d755b987dcad404fa Mon Sep 17 00:00:00 2001 From: Nikhil Shringarpurey Date: Fri, 10 Sep 2021 14:22:21 -0700 Subject: UI: Quad View Option Names Improved Improvements to Quad View options' titles and descriptions. See D12381 for more details. Differential Revision: https://developer.blender.org/D12381 Reviewed by Hans Goudey and Campbell Barton --- source/blender/makesrna/intern/rna_space.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 7aee6944d8d..61d92d350ed 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4941,18 +4941,18 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_LOCK_ROTATION); - RNA_def_property_ui_text(prop, "Lock", "Lock view rotation in side views"); + RNA_def_property_ui_text(prop, "Lock Rotation", "Lock view rotation of side views to Top/Front/Right"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); prop = RNA_def_property(srna, "show_sync_view", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_BOXVIEW); - RNA_def_property_ui_text(prop, "Box", "Sync view position between side views"); + RNA_def_property_ui_text(prop, "Sync Zoom/Pan", "Sync view position between side views"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); prop = RNA_def_property(srna, "use_box_clip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_BOXCLIP); RNA_def_property_ui_text( - prop, "Clip", "Clip objects based on what's visible in other side views"); + prop, "Clip Contents", "Clip view contents based on what is visible in other side views"); RNA_def_property_update( prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_clip_update"); -- cgit v1.2.3 From cb833138633e1c402c87115e504c492a12451810 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 22:48:49 -0500 Subject: Nodes: Add vector min/max support to new socket builder API Also use it to fix an incorrect min and max in the cube mesh primitive node. --- source/blender/nodes/NOD_socket_declarations.hh | 14 ++++++++++++++ .../nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc | 2 +- source/blender/nodes/intern/node_socket_declarations.cc | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 1a5873c37b5..11352111356 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -102,6 +102,8 @@ class Int : public SocketDeclaration { class Vector : public SocketDeclaration { private: float3 default_value_ = {0, 0, 0}; + float soft_min_value_ = -FLT_MAX; + float soft_max_value_ = FLT_MAX; PropertySubType subtype_ = PROP_NONE; public: @@ -117,6 +119,18 @@ class Vector : public SocketDeclaration { return *this; } + Vector &min(const float min) + { + soft_min_value_ = min; + return *this; + } + + Vector &max(const float max) + { + soft_max_value_ = max; + return *this; + } + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index f5d9ed2d6a9..af8ce02b3c1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -26,7 +26,7 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) { - b.add_input("Size").default_value({1.0f, 1.0f, 1.0f}).subtype(PROP_TRANSLATION); + b.add_input("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); b.add_input("Vertices X").default_value(2).min(2).max(1000); b.add_input("Vertices Y").default_value(2).min(2).max(1000); b.add_input("Vertices Z").default_value(2).min(2).max(1000); diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index ba9a1870b0c..e17b0fd5579 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -148,6 +148,8 @@ bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ou &ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; copy_v3_v3(value.value, default_value_); + value.min = soft_min_value_; + value.max = soft_max_value_; return socket; } -- cgit v1.2.3 From d475f994606e38a69cab4e2b64c9237d0ee62726 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 22:58:30 -0500 Subject: Geometry Nodes: Support "Evaluated" mode in Resample Curve node Just like the curve to points node, this mode outputs point locations based on the input curve's evaluated points (the points you see in the viewport). This is expected to be faster, since it doesn't have to sample equal positions on the result, and it's also consistent with the existing choices in the curve to points node. --- source/blender/makesrna/intern/rna_nodetree.c | 6 ++++ .../geometry/nodes/node_geo_curve_resample.cc | 39 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b294e93310c..86e134799aa 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -10091,6 +10091,12 @@ static void def_geo_curve_resample(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_EVALUATED, + "EVALUATED", + 0, + "Evaluated", + "Output the input spline's evaluated points, based on the resolution attribute for NURBS " + "and Bezier splines. Poly splines are unchanged"}, {GEO_NODE_CURVE_SAMPLE_COUNT, "COUNT", 0, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 6f40687540c..447f2989308 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -42,7 +42,7 @@ static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); } static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) @@ -148,6 +148,36 @@ static SplinePtr resample_spline(const Spline &input_spline, const int count) return output_spline; } +static SplinePtr resample_spline_evaluated(const Spline &src) +{ + std::unique_ptr dst = std::make_unique(); + Spline::copy_base_settings(src, *dst); + dst->resize(src.evaluated_points_size()); + + dst->positions().copy_from(src.evaluated_positions()); + dst->positions().copy_from(src.evaluated_positions()); + src.interpolate_to_evaluated(src.radii())->materialize(dst->radii()); + src.interpolate_to_evaluated(src.tilts())->materialize(dst->tilts()); + + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional dst_attribute = dst->attributes.get_for_write(attribute_id); + if (dst_attribute) { + src.interpolate_to_evaluated(*src_attribute)->materialize(dst_attribute->data()); + return true; + } + } + + BLI_assert_unreachable(); + return true; + }, + ATTR_DOMAIN_POINT); + + return dst; +} + static std::unique_ptr resample_curve(const CurveEval &input_curve, const SampleModeParam &mode_param) { @@ -174,6 +204,13 @@ static std::unique_ptr resample_curve(const CurveEval &input_curve, } }); } + else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_EVALUATED) { + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + }); + } output_curve->attributes = input_curve.attributes; -- cgit v1.2.3 From 6ae8de474299ec4c7c5bf2439e97998779ed4221 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 10 Sep 2021 23:21:58 -0500 Subject: Cleanup: Rename variables, simplify logic Mostly renaming the variables to improve line wrapping. But also the "foreach_attribute" loops look simpler now. Also use `Spline::copy_base_settings` and don't bother with an extra call to reallocate the attribute arrays. --- .../geometry/nodes/node_geo_curve_resample.cc | 97 +++++++++------------- 1 file changed, 41 insertions(+), 56 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 447f2989308..e89d500fe57 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -72,80 +72,65 @@ struct SampleModeParam { std::optional count; }; -static SplinePtr resample_spline(const Spline &input_spline, const int count) +static SplinePtr resample_spline(const Spline &src, const int count) { - std::unique_ptr output_spline = std::make_unique(); - output_spline->set_cyclic(input_spline.is_cyclic()); - output_spline->normal_mode = input_spline.normal_mode; - - if (input_spline.evaluated_edges_size() < 1 || count == 1) { - output_spline->add_point(input_spline.positions().first(), - input_spline.tilts().first(), - input_spline.radii().first()); - output_spline->attributes.reallocate(1); - input_spline.attributes.foreach_attribute( + std::unique_ptr dst = std::make_unique(); + Spline::copy_base_settings(src, *dst); + + if (src.evaluated_edges_size() < 1 || count == 1) { + dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); + dst->attributes.reallocate(1); + src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional src = input_spline.attributes.get_for_read(attribute_id); - BLI_assert(src); - if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; + std::optional src_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional dst_attribute = dst->attributes.get_for_write( + attribute_id); + if (dst_attribute) { + src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); + return true; + } } - std::optional dst = output_spline->attributes.get_for_write(attribute_id); - if (!dst) { - BLI_assert_unreachable(); - return false; - } - src->type().copy_assign(src->data(), dst->data()); - return true; + BLI_assert_unreachable(); + return false; }, ATTR_DOMAIN_POINT); - return output_spline; + return dst; } - output_spline->resize(count); + dst->resize(count); - Array uniform_samples = input_spline.sample_uniform_index_factors(count); + Array uniform_samples = src.sample_uniform_index_factors(count); - input_spline.sample_with_index_factors( - input_spline.evaluated_positions(), uniform_samples, output_spline->positions()); + src.sample_with_index_factors( + src.evaluated_positions(), uniform_samples, dst->positions()); - input_spline.sample_with_index_factors( - input_spline.interpolate_to_evaluated(input_spline.radii()), - uniform_samples, - output_spline->radii()); + src.sample_with_index_factors( + src.interpolate_to_evaluated(src.radii()), uniform_samples, dst->radii()); - input_spline.sample_with_index_factors( - input_spline.interpolate_to_evaluated(input_spline.tilts()), - uniform_samples, - output_spline->tilts()); + src.sample_with_index_factors( + src.interpolate_to_evaluated(src.tilts()), uniform_samples, dst->tilts()); - output_spline->attributes.reallocate(count); - input_spline.attributes.foreach_attribute( + src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional input_attribute = input_spline.attributes.get_for_read(attribute_id); - BLI_assert(input_attribute); - if (!output_spline->attributes.create(attribute_id, meta_data.data_type)) { - BLI_assert_unreachable(); - return false; - } - std::optional output_attribute = output_spline->attributes.get_for_write( - attribute_id); - if (!output_attribute) { - BLI_assert_unreachable(); - return false; + std::optional input_attribute = src.attributes.get_for_read(attribute_id); + if (dst->attributes.create(attribute_id, meta_data.data_type)) { + std::optional output_attribute = dst->attributes.get_for_write( + attribute_id); + if (output_attribute) { + src.sample_with_index_factors(*src.interpolate_to_evaluated(*input_attribute), + uniform_samples, + *output_attribute); + return true; + } } - input_spline.sample_with_index_factors( - *input_spline.interpolate_to_evaluated(*input_attribute), - uniform_samples, - *output_attribute); - - return true; + BLI_assert_unreachable(); + return false; }, ATTR_DOMAIN_POINT); - return output_spline; + return dst; } static SplinePtr resample_spline_evaluated(const Spline &src) -- cgit v1.2.3 From aeeffb935e9406fa2cdcb84828aa0e498b2df664 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 11 Sep 2021 11:43:59 +0200 Subject: Functions: store cursors to previous instructions Now an instruction knows the cursors where it is inserted instead of just the instruction that references it. This has two benefits: * An instruction knows when it is the entry instruction. * The cursor can contain more information, e.g. if it is linked to the true or false branch of a branch instruction. This also simplifies updating the procedure in future optimization passes. --- .../functions/FN_multi_function_procedure.hh | 105 +++++++++++++++++++-- .../FN_multi_function_procedure_builder.hh | 61 +----------- .../functions/intern/multi_function_procedure.cc | 102 ++++++++++++++++---- .../intern/multi_function_procedure_builder.cc | 44 --------- 4 files changed, 183 insertions(+), 129 deletions(-) diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh index 62f2292c1d9..4c06ce98ee3 100644 --- a/source/blender/functions/FN_multi_function_procedure.hh +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -42,6 +42,55 @@ enum class MFInstructionType { Return, }; +/** + * An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction + * can be inserted. + */ +class MFInstructionCursor { + public: + enum Type { + None, + Entry, + Call, + Destruct, + Branch, + Dummy, + }; + + private: + Type type_ = None; + MFInstruction *instruction_ = nullptr; + /* Only used when it is a branch instruction. */ + bool branch_output_ = false; + + public: + MFInstructionCursor() = default; + MFInstructionCursor(MFCallInstruction &instruction); + MFInstructionCursor(MFDestructInstruction &instruction); + MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output); + MFInstructionCursor(MFDummyInstruction &instruction); + + static MFInstructionCursor ForEntry(); + + MFInstruction *next(MFProcedure &procedure) const; + void set_next(MFProcedure &procedure, MFInstruction *new_instruction) const; + + MFInstruction *instruction() const; + + Type type() const; + + friend bool operator==(const MFInstructionCursor &a, const MFInstructionCursor &b) + { + return a.type_ == b.type_ && a.instruction_ == b.instruction_ && + a.branch_output_ == b.branch_output_; + } + + friend bool operator!=(const MFInstructionCursor &a, const MFInstructionCursor &b) + { + return !(a == b); + } +}; + /** * A variable is similar to a virtual register in other libraries. During evaluation, every is * either uninitialized or contains a value for every index (remember, a multi-function procedure @@ -73,7 +122,7 @@ class MFVariable : NonCopyable, NonMovable { class MFInstruction : NonCopyable, NonMovable { protected: MFInstructionType type_; - Vector prev_; + Vector prev_; friend MFProcedure; friend MFCallInstruction; @@ -89,8 +138,7 @@ class MFInstruction : NonCopyable, NonMovable { * Other instructions that come before this instruction. There can be multiple previous * instructions when branching is used in the procedure. */ - Span prev(); - Span prev() const; + Span prev() const; }; /** @@ -275,6 +323,50 @@ using MFDestructInstruction = fn::MFDestructInstruction; using MFProcedure = fn::MFProcedure; } // namespace multi_function_procedure_types +/* -------------------------------------------------------------------- + * MFInstructionCursor inline methods. + */ + +inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) + : type_(Call), instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction) + : type_(Destruct), instruction_(&instruction) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction, + bool branch_output) + : type_(Branch), instruction_(&instruction), branch_output_(branch_output) +{ +} + +inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction) + : type_(Dummy), instruction_(&instruction) +{ +} + +inline MFInstructionCursor MFInstructionCursor::ForEntry() +{ + MFInstructionCursor cursor; + cursor.type_ = Type::Entry; + return cursor; +} + +inline MFInstruction *MFInstructionCursor::instruction() const +{ + /* This isn't really const correct unfortunately, because to make it correct we'll need a const + * version of #MFInstructionCursor. */ + return instruction_; +} + +inline MFInstructionCursor::Type MFInstructionCursor::type() const +{ + return type_; +} + /* -------------------------------------------------------------------- * MFVariable inline methods. */ @@ -308,12 +400,7 @@ inline MFInstructionType MFInstruction::type() const return type_; } -inline Span MFInstruction::prev() -{ - return prev_; -} - -inline Span MFInstruction::prev() const +inline Span MFInstruction::prev() const { return prev_; } diff --git a/source/blender/functions/FN_multi_function_procedure_builder.hh b/source/blender/functions/FN_multi_function_procedure_builder.hh index d5e45470a0e..e416f7e500d 100644 --- a/source/blender/functions/FN_multi_function_procedure_builder.hh +++ b/source/blender/functions/FN_multi_function_procedure_builder.hh @@ -24,31 +24,6 @@ namespace blender::fn { -/** - * An #MFInstructionCursor points to a position in a multi-function procedure, where an instruction - * can be inserted. - */ -class MFInstructionCursor { - private: - MFInstruction *instruction_ = nullptr; - /* Only used when it is a branch instruction. */ - bool branch_output_ = false; - /* Only used when instruction is null. */ - bool is_entry_ = false; - - public: - MFInstructionCursor() = default; - - MFInstructionCursor(MFCallInstruction &instruction); - MFInstructionCursor(MFDestructInstruction &instruction); - MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output); - MFInstructionCursor(MFDummyInstruction &instruction); - - static MFInstructionCursor Entry(); - - void insert(MFProcedure &procedure, MFInstruction *new_instruction); -}; - /** * Utility class to build a #MFProcedure. */ @@ -64,7 +39,7 @@ class MFProcedureBuilder { struct Loop; MFProcedureBuilder(MFProcedure &procedure, - MFInstructionCursor initial_cursor = MFInstructionCursor::Entry()); + MFInstructionCursor initial_cursor = MFInstructionCursor::ForEntry()); MFProcedureBuilder(Span builders); @@ -121,38 +96,6 @@ struct MFProcedureBuilder::Loop { MFDummyInstruction *end = nullptr; }; -/* -------------------------------------------------------------------- - * MFInstructionCursor inline methods. - */ - -inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) - : instruction_(&instruction) -{ -} - -inline MFInstructionCursor::MFInstructionCursor(MFDestructInstruction &instruction) - : instruction_(&instruction) -{ -} - -inline MFInstructionCursor::MFInstructionCursor(MFBranchInstruction &instruction, - bool branch_output) - : instruction_(&instruction), branch_output_(branch_output) -{ -} - -inline MFInstructionCursor::MFInstructionCursor(MFDummyInstruction &instruction) - : instruction_(&instruction) -{ -} - -inline MFInstructionCursor MFInstructionCursor::Entry() -{ - MFInstructionCursor cursor; - cursor.is_entry_ = true; - return cursor; -} - /* -------------------------------------------------------------------- * MFProcedureBuilder inline methods. */ @@ -253,7 +196,7 @@ inline void MFProcedureBuilder::add_output_parameter(MFVariable &variable) inline void MFProcedureBuilder::link_to_cursors(MFInstruction *instruction) { for (MFInstructionCursor &cursor : cursors_) { - cursor.insert(*procedure_, instruction); + cursor.set_next(*procedure_, instruction); } } diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index 6eff7bc09f8..e024cee79c8 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -21,6 +21,65 @@ namespace blender::fn { +void MFInstructionCursor::set_next(MFProcedure &procedure, MFInstruction *new_instruction) const +{ + switch (type_) { + case Type::None: { + break; + } + case Type::Entry: { + procedure.set_entry(*new_instruction); + break; + } + case Type::Call: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + case Type::Branch: { + MFBranchInstruction &branch_instruction = *static_cast(instruction_); + if (branch_output_) { + branch_instruction.set_branch_true(new_instruction); + } + else { + branch_instruction.set_branch_false(new_instruction); + } + break; + } + case Type::Destruct: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + case Type::Dummy: { + static_cast(instruction_)->set_next(new_instruction); + break; + } + } +} + +MFInstruction *MFInstructionCursor::next(MFProcedure &procedure) const +{ + switch (type_) { + case Type::None: + return nullptr; + case Type::Entry: + return procedure.entry(); + case Type::Call: + return static_cast(instruction_)->next(); + case Type::Branch: { + MFBranchInstruction &branch_instruction = *static_cast(instruction_); + if (branch_output_) { + return branch_instruction.branch_true(); + } + return branch_instruction.branch_false(); + } + case Type::Destruct: + return static_cast(instruction_)->next(); + case Type::Dummy: + return static_cast(instruction_)->next(); + } + return nullptr; +} + void MFVariable::set_name(std::string name) { name_ = std::move(name); @@ -29,10 +88,10 @@ void MFVariable::set_name(std::string name) void MFCallInstruction::set_next(MFInstruction *instruction) { if (next_ != nullptr) { - next_->prev_.remove_first_occurrence_and_reorder(this); + next_->prev_.remove_first_occurrence_and_reorder(*this); } if (instruction != nullptr) { - instruction->prev_.append(this); + instruction->prev_.append(*this); } next_ = instruction; } @@ -71,10 +130,10 @@ void MFBranchInstruction::set_condition(MFVariable *variable) void MFBranchInstruction::set_branch_true(MFInstruction *instruction) { if (branch_true_ != nullptr) { - branch_true_->prev_.remove_first_occurrence_and_reorder(this); + branch_true_->prev_.remove_first_occurrence_and_reorder({*this, true}); } if (instruction != nullptr) { - instruction->prev_.append(this); + instruction->prev_.append({*this, true}); } branch_true_ = instruction; } @@ -82,10 +141,10 @@ void MFBranchInstruction::set_branch_true(MFInstruction *instruction) void MFBranchInstruction::set_branch_false(MFInstruction *instruction) { if (branch_false_ != nullptr) { - branch_false_->prev_.remove_first_occurrence_and_reorder(this); + branch_false_->prev_.remove_first_occurrence_and_reorder({*this, false}); } if (instruction != nullptr) { - instruction->prev_.append(this); + instruction->prev_.append({*this, false}); } branch_false_ = instruction; } @@ -104,10 +163,10 @@ void MFDestructInstruction::set_variable(MFVariable *variable) void MFDestructInstruction::set_next(MFInstruction *instruction) { if (next_ != nullptr) { - next_->prev_.remove_first_occurrence_and_reorder(this); + next_->prev_.remove_first_occurrence_and_reorder(*this); } if (instruction != nullptr) { - instruction->prev_.append(this); + instruction->prev_.append(*this); } next_ = instruction; } @@ -115,10 +174,10 @@ void MFDestructInstruction::set_next(MFInstruction *instruction) void MFDummyInstruction::set_next(MFInstruction *instruction) { if (next_ != nullptr) { - next_->prev_.remove_first_occurrence_and_reorder(this); + next_->prev_.remove_first_occurrence_and_reorder(*this); } if (instruction != nullptr) { - instruction->prev_.append(this); + instruction->prev_.append(*this); } next_ = instruction; } @@ -183,7 +242,11 @@ void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVar void MFProcedure::set_entry(MFInstruction &entry) { + if (entry_ != NULL) { + entry_->prev_.remove_first_occurrence_and_reorder(MFInstructionCursor::ForEntry()); + } entry_ = &entry; + entry_->prev_.append(MFInstructionCursor::ForEntry()); } MFProcedure::~MFProcedure() @@ -420,7 +483,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction Set checked_instructions; Stack instructions_to_check; - instructions_to_check.push_multiple(target_instruction.prev_); + for (const MFInstructionCursor &cursor : target_instruction.prev_) { + if (cursor.instruction() != nullptr) { + instructions_to_check.push(cursor.instruction()); + } + } while (!instructions_to_check.is_empty()) { const MFInstruction &instruction = *instructions_to_check.pop(); @@ -467,7 +534,11 @@ MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction if (&instruction == entry_) { check_entry_instruction(); } - instructions_to_check.push_multiple(instruction.prev_); + for (const MFInstructionCursor &cursor : instruction.prev_) { + if (cursor.instruction() != nullptr) { + instructions_to_check.push(cursor.instruction()); + } + } } } @@ -607,13 +678,10 @@ class MFProcedureDotExport { bool has_to_be_block_begin(const MFInstruction &instruction) { - if (procedure_.entry() == &instruction) { - return true; - } if (instruction.prev().size() != 1) { return true; } - if (instruction.prev()[0]->type() == MFInstructionType::Branch) { + if (instruction.prev()[0].type() == MFInstructionCursor::Type::Branch) { return true; } return false; @@ -623,7 +691,7 @@ class MFProcedureDotExport { { const MFInstruction *current = &representative; while (!this->has_to_be_block_begin(*current)) { - current = current->prev()[0]; + current = current->prev()[0].instruction(); if (current == &representative) { /* There is a loop without entry or exit, just break it up here. */ break; diff --git a/source/blender/functions/intern/multi_function_procedure_builder.cc b/source/blender/functions/intern/multi_function_procedure_builder.cc index 3c088776bea..d30e6c0e14a 100644 --- a/source/blender/functions/intern/multi_function_procedure_builder.cc +++ b/source/blender/functions/intern/multi_function_procedure_builder.cc @@ -18,50 +18,6 @@ namespace blender::fn { -void MFInstructionCursor::insert(MFProcedure &procedure, MFInstruction *new_instruction) -{ - if (instruction_ == nullptr) { - if (is_entry_) { - procedure.set_entry(*new_instruction); - } - else { - /* The cursors points at nothing, nothing to do. */ - } - } - else { - switch (instruction_->type()) { - case MFInstructionType::Call: { - static_cast(instruction_)->set_next(new_instruction); - break; - } - case MFInstructionType::Branch: { - MFBranchInstruction &branch_instruction = *static_cast( - instruction_); - if (branch_output_) { - branch_instruction.set_branch_true(new_instruction); - } - else { - branch_instruction.set_branch_false(new_instruction); - } - break; - } - case MFInstructionType::Destruct: { - static_cast(instruction_)->set_next(new_instruction); - break; - } - case MFInstructionType::Dummy: { - static_cast(instruction_)->set_next(new_instruction); - break; - } - case MFInstructionType::Return: { - /* It shouldn't be possible to build a cursor that points to a return instruction. */ - BLI_assert_unreachable(); - break; - } - } - } -} - void MFProcedureBuilder::add_destruct(MFVariable &variable) { MFDestructInstruction &instruction = procedure_->new_destruct_instruction(); -- cgit v1.2.3 From 166c8be7ac018c461514453b9947a23b7ac2ef26 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 11 Sep 2021 12:41:46 +0200 Subject: Cleanup: use nullptr --- source/blender/functions/intern/multi_function_procedure.cc | 2 +- source/blender/io/alembic/intern/abc_reader_mesh.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index e024cee79c8..2aa760a494f 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -242,7 +242,7 @@ void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVar void MFProcedure::set_entry(MFInstruction &entry) { - if (entry_ != NULL) { + if (entry_ != nullptr) { entry_->prev_.remove_first_occurrence_and_reorder(MFInstructionCursor::ForEntry()); } entry_ = &entry; diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index dbccc8f2851..eab94139f55 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -457,7 +457,7 @@ static void read_velocity(const V3fArraySamplePtr &velocities, const float velocity_scale) { CustomDataLayer *velocity_layer = BKE_id_attribute_new( - &config.mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, NULL); + &config.mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, nullptr); float(*velocity)[3] = (float(*)[3])velocity_layer->data; const int num_velocity_vectors = static_cast(velocities->size()); -- cgit v1.2.3 From 4e78b89e487e9b9707d583c6b2578ad122c59d5e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 11 Sep 2021 13:05:20 +0200 Subject: Geometry Nodes: add field support for socket inspection Since fields were committed to master, socket inspection did not work correctly for all socket types anymore. Now the same functionality as before is back. Furthermore, fields that depend on some input will now show the inputs in the socket inspection. I added support for evaluating constant fields more immediately. This has the benefit that the same constant field is not evaluated more than once. It also helps with making the field independent of the multi-functions that it uses. We might still want to change the ownership handling for the multi-functions of nodes a bit, but that can be done separately. Differential Revision: https://developer.blender.org/D12444 --- source/blender/blenkernel/BKE_geometry_set.hh | 9 +++ .../blender/blenkernel/intern/attribute_access.cc | 16 +++++ source/blender/editors/space_node/node_draw.cc | 83 +++++++++++++++++----- source/blender/functions/FN_field.hh | 59 +++++++++++---- .../blender/functions/FN_multi_function_builder.hh | 7 +- source/blender/functions/intern/field.cc | 72 +++++++++++++++++++ .../functions/intern/multi_function_builder.cc | 21 +++++- .../functions/tests/FN_multi_function_test.cc | 2 +- .../modifiers/intern/MOD_nodes_evaluator.cc | 10 +-- 9 files changed, 237 insertions(+), 42 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index c3d594d7dcd..3da35cb4fe1 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -657,10 +657,17 @@ class AttributeFieldInput : public fn::FieldInput { { } + StringRefNull attribute_name() const + { + return name_; + } + const GVArray *get_varray_for_context(const fn::FieldContext &context, IndexMask mask, ResourceScope &scope) const override; + std::string socket_inspection_name() const override; + uint64_t hash() const override; bool is_equal_to(const fn::FieldNode &other) const override; }; @@ -683,6 +690,8 @@ class AnonymousAttributeFieldInput : public fn::FieldInput { IndexMask mask, ResourceScope &scope) const override; + std::string socket_inspection_name() const override; + uint64_t hash() const override; bool is_equal_to(const fn::FieldNode &other) const override; }; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 08bd5dc5981..2a5bb99a18b 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -32,6 +32,8 @@ #include "BLI_float2.hh" #include "BLI_span.hh" +#include "BLT_translation.h" + #include "CLG_log.h" #include "NOD_type_conversions.hh" @@ -1318,6 +1320,13 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex return nullptr; } +std::string AttributeFieldInput::socket_inspection_name() const +{ + std::stringstream ss; + ss << TIP_("Attribute: ") << name_; + return ss.str(); +} + uint64_t AttributeFieldInput::hash() const { return get_default_hash_2(name_, type_); @@ -1346,6 +1355,13 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( return nullptr; } +std::string AnonymousAttributeFieldInput::socket_inspection_name() const +{ + std::stringstream ss; + ss << TIP_("Anonymous Attribute: ") << debug_name_; + return ss.str(); +} + uint64_t AnonymousAttributeFieldInput::hash() const { return get_default_hash_2(anonymous_id_.get(), type_); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 5b4e3b3b6f5..aa241071425 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -41,10 +41,12 @@ #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" +#include "BLI_vector_set.hh" #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_geometry_set.hh" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -78,6 +80,8 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "FN_field_cpp_type.hh" + #include "node_intern.h" /* own include */ #ifdef WITH_COMPOSITOR @@ -88,7 +92,11 @@ using blender::Map; using blender::Set; using blender::Span; using blender::Vector; +using blender::VectorSet; using blender::fn::CPPType; +using blender::fn::FieldCPPType; +using blender::fn::FieldInput; +using blender::fn::GField; using blender::fn::GPointer; namespace geo_log = blender::nodes::geometry_nodes_eval_log; @@ -850,31 +858,70 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal }; const GPointer value = value_log.value(); - if (value.is_type()) { - ss << *value.get() << TIP_(" (Integer)"); - } - else if (value.is_type()) { - ss << *value.get() << TIP_(" (Float)"); - } - else if (value.is_type()) { - ss << *value.get() << TIP_(" (Vector)"); - } - else if (value.is_type()) { - ss << (*value.get() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (value.is_type()) { - ss << *value.get() << TIP_(" (String)"); + const CPPType &type = *value.type(); + if (const FieldCPPType *field_type = dynamic_cast(&type)) { + const CPPType &base_type = field_type->field_type(); + BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer); + const GField &field = field_type->get_gfield(value.get()); + if (field.node().depends_on_input()) { + if (base_type.is()) { + ss << TIP_("Integer Field"); + } + else if (base_type.is()) { + ss << TIP_("Float Field"); + } + else if (base_type.is()) { + ss << TIP_("Vector Field"); + } + else if (base_type.is()) { + ss << TIP_("Boolean Field"); + } + else if (base_type.is()) { + ss << TIP_("String Field"); + } + ss << TIP_(" based on:\n"); + + /* Use vector set to deduplicate inputs. */ + VectorSet> field_inputs; + field.node().foreach_field_input( + [&](const FieldInput &field_input) { field_inputs.add(field_input); }); + for (const FieldInput &field_input : field_inputs) { + ss << "\u2022 " << field_input.socket_inspection_name(); + if (field_input != field_inputs.as_span().last().get()) { + ss << ".\n"; + } + } + } + else { + blender::fn::evaluate_constant_field(field, buffer); + if (base_type.is()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (base_type.is()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (base_type.is()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (base_type.is()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (base_type.is()) { + ss << *(std::string *)buffer << TIP_(" (String)"); + } + base_type.destruct(buffer); + } } - else if (value.is_type()) { + else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_OB); } - else if (value.is_type()) { + else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_MA); } - else if (value.is_type()) { + else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_TE); } - else if (value.is_type()) { + else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_GR); } } diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 79a6faf499b..976d260d91b 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -46,6 +46,7 @@ * they share common sub-fields and a common context. */ +#include "BLI_function_ref.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" @@ -57,15 +58,27 @@ namespace blender::fn { +class FieldInput; + /** * A node in a field-tree. It has at least one output that can be referenced by fields. */ class FieldNode { private: bool is_input_; + /** + * True when this node is a #FieldInput or (potentially indirectly) depends on one. This could + * always be derived again later by traversing the field-tree, but keeping track of it while the + * field is built is cheaper. + * + * If this is false, the field is constant. Note that even when this is true, the field may be + * constant when all inputs are constant. + */ + bool depends_on_input_; public: - FieldNode(bool is_input) : is_input_(is_input) + FieldNode(bool is_input, bool depends_on_input) + : is_input_(is_input), depends_on_input_(depends_on_input) { } @@ -83,6 +96,17 @@ class FieldNode { return !is_input_; } + bool depends_on_input() const + { + return depends_on_input_; + } + + /** + * Invoke callback for every field input. It might be called multiple times for the same input. + * The caller is responsible for deduplication if required. + */ + virtual void foreach_field_input(FunctionRef foreach_fn) const = 0; + virtual uint64_t hash() const { return get_default_hash(this); @@ -93,6 +117,11 @@ class FieldNode { return a.is_equal_to(b); } + friend bool operator!=(const FieldNode &a, const FieldNode &b) + { + return !(a == b); + } + virtual bool is_equal_to(const FieldNode &other) const { return this == &other; @@ -211,16 +240,8 @@ class FieldOperation : public FieldNode { blender::Vector inputs_; public: - FieldOperation(std::unique_ptr function, Vector inputs = {}) - : FieldNode(false), owned_function_(std::move(function)), inputs_(std::move(inputs)) - { - function_ = owned_function_.get(); - } - - FieldOperation(const MultiFunction &function, Vector inputs = {}) - : FieldNode(false), function_(&function), inputs_(std::move(inputs)) - { - } + FieldOperation(std::unique_ptr function, Vector inputs = {}); + FieldOperation(const MultiFunction &function, Vector inputs = {}); Span inputs() const { @@ -247,6 +268,8 @@ class FieldOperation : public FieldNode { BLI_assert_unreachable(); return CPPType::get(); } + + void foreach_field_input(FunctionRef foreach_fn) const override; }; class FieldContext; @@ -260,10 +283,7 @@ class FieldInput : public FieldNode { std::string debug_name_; public: - FieldInput(const CPPType &type, std::string debug_name = "") - : FieldNode(true), type_(&type), debug_name_(std::move(debug_name)) - { - } + FieldInput(const CPPType &type, std::string debug_name = ""); /** * Get the value of this specific input based on the given context. The returned virtual array, @@ -273,6 +293,11 @@ class FieldInput : public FieldNode { IndexMask mask, ResourceScope &scope) const = 0; + virtual std::string socket_inspection_name() const + { + return debug_name_; + } + blender::StringRef debug_name() const { return debug_name_; @@ -289,6 +314,8 @@ class FieldInput : public FieldNode { UNUSED_VARS_NDEBUG(output_index); return *type_; } + + void foreach_field_input(FunctionRef foreach_fn) const override; }; /** @@ -453,4 +480,6 @@ template Field make_constant_field(T value) return Field{GField{std::move(operation), 0}}; } +GField make_field_constant_if_possible(GField field); + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index d13615ced07..0ce05cbca30 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -326,18 +326,21 @@ template class CustomMF_Convert : public MultiFuncti /** * A multi-function that outputs the same value every time. The value is not owned by an instance - * of this function. The caller is responsible for destructing and freeing the value. + * of this function. If #make_value_copy is false, the caller is responsible for destructing and + * freeing the value. */ class CustomMF_GenericConstant : public MultiFunction { private: const CPPType &type_; const void *value_; MFSignature signature_; + bool owns_value_; template friend class CustomMF_Constant; public: - CustomMF_GenericConstant(const CPPType &type, const void *value); + CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy); + ~CustomMF_GenericConstant(); void call(IndexMask mask, MFParams params, MFContext context) const override; uint64_t hash() const override; bool equals(const MultiFunction &other) const override; diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index fa7dea97b7f..6133658d8e3 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -462,6 +462,29 @@ void evaluate_constant_field(const GField &field, void *r_value) varrays[0]->get_to_uninitialized(0, r_value); } +/** + * If the field depends on some input, the same field is returned. + * Otherwise the field is evaluated and a new field is created that just computes this constant. + * + * Making the field constant has two benefits: + * - The field-tree becomes a single node, which is more efficient when the field is evaluated many + * times. + * - Memory of the input fields may be freed. + */ +GField make_field_constant_if_possible(GField field) +{ + if (field.node().depends_on_input()) { + return field; + } + const CPPType &type = field.cpp_type(); + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + evaluate_constant_field(field, buffer); + auto constant_fn = std::make_unique(type, buffer, true); + type.destruct(buffer); + auto operation = std::make_shared(std::move(constant_fn)); + return GField{operation, 0}; +} + const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, IndexMask mask, ResourceScope &scope) const @@ -471,6 +494,55 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, return field_input.get_varray_for_context(*this, mask, scope); } +/* -------------------------------------------------------------------- + * FieldOperation. + */ + +FieldOperation::FieldOperation(std::unique_ptr function, + Vector inputs) + : FieldOperation(*function, std::move(inputs)) +{ + owned_function_ = std::move(function); +} + +static bool any_field_depends_on_input(Span fields) +{ + for (const GField &field : fields) { + if (field.node().depends_on_input()) { + return true; + } + } + return false; +} + +FieldOperation::FieldOperation(const MultiFunction &function, Vector inputs) + : FieldNode(false, any_field_depends_on_input(inputs)), + function_(&function), + inputs_(std::move(inputs)) +{ +} + +void FieldOperation::foreach_field_input(FunctionRef foreach_fn) const +{ + for (const GField &field : inputs_) { + field.node().foreach_field_input(foreach_fn); + } +} + +/* -------------------------------------------------------------------- + * FieldInput. + */ + +FieldInput::FieldInput(const CPPType &type, std::string debug_name) + : FieldNode(true, true), type_(&type), debug_name_(std::move(debug_name)) +{ +} + +void FieldInput::foreach_field_input(FunctionRef foreach_fn) const +{ + foreach_fn(*this); +} + /* -------------------------------------------------------------------- * FieldEvaluator. */ diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index 180d1f17a54..f891f162820 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -20,9 +20,18 @@ namespace blender::fn { -CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const void *value) - : type_(type), value_(value) +CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, + const void *value, + bool make_value_copy) + : type_(type), owns_value_(make_value_copy) { + if (make_value_copy) { + void *copied_value = MEM_mallocN_aligned(type.size(), type.alignment(), __func__); + type.copy_construct(value, copied_value); + value = copied_value; + } + value_ = value; + MFSignatureBuilder signature{"Constant " + type.name()}; std::stringstream ss; type.print_or_default(value, ss, type.name()); @@ -31,6 +40,14 @@ CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const vo this->set_signature(&signature_); } +CustomMF_GenericConstant::~CustomMF_GenericConstant() +{ + if (owns_value_) { + signature_.param_types[0].data_type().single_type().destruct((void *)value_); + MEM_freeN((void *)value_); + } +} + void CustomMF_GenericConstant::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 91c72a51dd6..204dbfab6aa 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -263,7 +263,7 @@ TEST(multi_function, CustomMF_Constant) TEST(multi_function, CustomMF_GenericConstant) { int value = 42; - CustomMF_GenericConstant fn{CPPType::get(), (const void *)&value}; + CustomMF_GenericConstant fn{CPPType::get(), (const void *)&value, false}; EXPECT_EQ(fn.param_name(0), "42"); Array outputs(4, 0); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 4f21924619b..f67f7f967c9 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -892,8 +892,10 @@ class GeometryNodesEvaluator { OutputState &output_state = node_state.outputs[i]; const DOutputSocket socket{node.context(), &socket_ref}; const CPPType *cpp_type = get_socket_cpp_type(socket_ref); - GField &field = *allocator.construct(operation, output_index).release(); - this->forward_output(socket, {cpp_type, &field}); + GField new_field{operation, output_index}; + new_field = fn::make_field_constant_if_possible(std::move(new_field)); + GField &field_to_forward = *allocator.construct(std::move(new_field)).release(); + this->forward_output(socket, {cpp_type, &field_to_forward}); output_state.has_been_computed = true; output_index++; } @@ -1411,8 +1413,8 @@ class GeometryNodesEvaluator { { if (const FieldCPPType *field_cpp_type = dynamic_cast(&type)) { const CPPType &base_type = field_cpp_type->field_type(); - auto constant_fn = std::make_unique(base_type, - base_type.default_value()); + auto constant_fn = std::make_unique( + base_type, base_type.default_value(), false); auto operation = std::make_shared(std::move(constant_fn)); new (r_value) GField(std::move(operation), 0); return; -- cgit v1.2.3 From cbe05edde59637a3e5a34ca0794aef2ad56faae2 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 11 Sep 2021 15:11:59 +0200 Subject: Compositor: Fix crash when hashing unconnected operations It was causing some tests to fail when enabling Full Frame mode. --- source/blender/compositor/intern/COM_NodeOperation.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index a87485fd51c..3bbd1b22d60 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -63,6 +63,10 @@ std::optional NodeOperation::generate_hash() hash.parents_hash_ = 0; for (NodeOperationInput &socket : m_inputs) { + if (!socket.isConnected()) { + continue; + } + NodeOperation &input = socket.getLink()->getOperation(); const bool is_constant = input.get_flags().is_constant_operation; combine_hashes(hash.parents_hash_, get_default_hash(is_constant)); -- cgit v1.2.3 From 863460e5a5cb196a8c6ca1050a2dbc1efd7957e0 Mon Sep 17 00:00:00 2001 From: Manuel Castilla Date: Sat, 11 Sep 2021 15:12:22 +0200 Subject: Compositor: Full frame previews Adds full frame implementation to PreviewOperation. Part of T88150. --- .../compositor/operations/COM_PreviewOperation.cc | 39 ++++++++++++++++++++++ .../compositor/operations/COM_PreviewOperation.h | 9 +++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index e7c11613aa3..fa8b5ffcabf 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -171,4 +171,43 @@ eCompositorPriority PreviewOperation::getRenderPriority() const return eCompositorPriority::Low; } +void PreviewOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + + r_input_area.xmin = output_area.xmin / m_divider; + r_input_area.xmax = output_area.xmax / m_divider; + r_input_area.ymin = output_area.ymin / m_divider; + r_input_area.ymax = output_area.ymax / m_divider; +} + +void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span inputs) +{ + MemoryBuffer *input = inputs[0]; + struct ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new( + m_viewSettings, m_displaySettings); + + rcti buffer_area; + BLI_rcti_init(&buffer_area, 0, this->getWidth(), 0, this->getHeight()); + BuffersIteratorBuilder it_builder( + m_outputBuffer, buffer_area, area, COM_data_type_num_channels(DataType::Color)); + + for (BuffersIterator it = it_builder.build(); !it.is_end(); ++it) { + const float rx = it.x / m_divider; + const float ry = it.y / m_divider; + + float color[4]; + input->read_elem_checked(rx, ry, color); + IMB_colormanagement_processor_apply_v4(cm_processor, color); + rgba_float_to_uchar(it.out, color); + } + + IMB_colormanagement_processor_free(cm_processor); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 0f43f01c5d6..05dae9c4dd8 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -20,13 +20,13 @@ #include "BKE_global.h" #include "BLI_rect.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_color_types.h" #include "DNA_image_types.h" namespace blender::compositor { -class PreviewOperation : public NodeOperation { +class PreviewOperation : public MultiThreadedOperation { protected: unsigned char *m_outputBuffer; @@ -63,6 +63,11 @@ class PreviewOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor -- cgit v1.2.3 From c946fdb2e5fc1eab139f70f01ea4e673435455bd Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Sat, 11 Sep 2021 09:06:47 -0700 Subject: Calm Warning: Unused Variable Calms warning for unused variable in `constraint_copy_to_selected_poll` by using UNUSED_VARS. See D12453 for further details Differential Revision: https://developer.blender.org/D12453 Reviewed by Campbell Barton --- source/blender/editors/object/object_constraint.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index c46e868899c..8702b18a46f 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -1786,7 +1786,8 @@ static bool constraint_copy_to_selected_poll(bContext *C) if (pchan) { bool found = false; - CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) { + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) { + UNUSED_VARS(ob); if (pchan != chan) { /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated * a list that needs to be freed by CTX_DATA_END. */ -- cgit v1.2.3 From b9febb54a492ac6c93802fb0aa189d4c3fd99b0b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 11 Sep 2021 13:54:40 -0500 Subject: Geometry Nodes: Support modifier on curve objects With this commit, curve objects support the geometry nodes modifier. Curves objects now evaluate to `CurveEval` unless there was a previous implicit conversion (tessellating modifiers, mesh modifiers, or the settings in the curve "Geometry" panel). In the new code, curves are only considered to be the wire edges-- any generated surface is a mesh instead, stored in the evaluated geometry set. The consolidation of concepts mentioned above allows remove a lot of code that had to do with maintaining the `DispList` type temporarily for modifiers and rendering. Instead, render engines see a separate object for the mesh from the mesh geometry component, and when the curve object evaluates to a curve, the `CurveEval` is always used for drawing wire edges. However, currently the `DispList` type is still maintained and used as an intermediate step in implicit mesh conversion. In the future, more uses of it could be changed to use `CurveEval` and `Mesh` instead. This is mostly not changed behavior, it is just a formalization of existing logic after recent fixes for 2.8 versions last year and two years ago. Also, in the future more functionality can be converted to nodes, removing cases of implicit conversions. For more discussion on that topic, see T89676. The `use_fill_deform` option is removed. It has not worked properly since 2.62, and the choice for filling a curve before or after deformation will work much better and be clearer with a node system. Applying the geometry nodes modifier to generate a curve is not implemented with this commit, so applying the modifier won't work at all. This is a separate technical challenge, and should be solved in a separate step. Differential Revision: https://developer.blender.org/D11597 --- .../scripts/startup/bl_operators/geometry_nodes.py | 4 +- .../scripts/startup/bl_ui/properties_data_curve.py | 1 - source/blender/blenkernel/BKE_displist.h | 2 +- source/blender/blenkernel/intern/displist.cc | 328 ++++++++------------- .../blenkernel/intern/geometry_component_curve.cc | 2 + source/blender/blenkernel/intern/geometry_set.cc | 2 +- .../blenkernel/intern/geometry_set_instances.cc | 13 - source/blender/blenkernel/intern/mesh_convert.c | 36 ++- source/blender/blenkernel/intern/object.c | 5 + source/blender/blenkernel/intern/object_dupli.cc | 2 +- .../intern/eval/deg_eval_runtime_backup_object.cc | 10 +- .../draw/engines/overlay/overlay_edit_text.c | 13 +- .../draw/engines/overlay/overlay_wireframe.c | 10 +- source/blender/draw/intern/draw_cache.c | 199 ++----------- source/blender/draw/intern/draw_cache.h | 14 - .../blender/draw/intern/draw_cache_impl_curve.cc | 153 ++-------- source/blender/editors/curve/editcurve_add.c | 1 - .../editors/space_spreadsheet/space_spreadsheet.cc | 2 +- .../blender/io/alembic/intern/abc_reader_curves.cc | 2 +- source/blender/io/usd/intern/usd_reader_curve.cc | 2 +- source/blender/io/usd/intern/usd_reader_nurbs.cc | 2 +- source/blender/makesdna/DNA_curve_types.h | 10 +- source/blender/makesdna/DNA_object_types.h | 3 +- source/blender/makesrna/intern/rna_curve.c | 6 - source/blender/modifiers/intern/MOD_nodes.cc | 7 +- 25 files changed, 237 insertions(+), 592 deletions(-) diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index ec2887a1a74..258a73bd70b 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -42,8 +42,8 @@ def geometry_node_group_empty_new(): def geometry_modifier_poll(context): ob = context.object - # Test object support for geometry node modifier (No curve, or hair object support yet) - if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME'}: + # Test object support for geometry node modifier (No hair object support yet) + if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT'}: return False return True diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 85f672cd50f..e5b675db2c5 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -120,7 +120,6 @@ class DATA_PT_shape_curve(CurveButtonsPanel, Panel): sub = col.column() sub.active = (curve.dimensions == '2D' or (curve.bevel_mode != 'OBJECT' and curve.dimensions == '3D')) sub.prop(curve, "fill_mode") - col.prop(curve, "use_fill_deform") if is_curve: col = layout.column() diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 0f37ba6c4ce..37e144ebbd3 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -99,7 +99,7 @@ void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph, struct Object *ob, struct ListBase *dispbase); -bool BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, +void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, struct ListBase *source_nurb, diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 58509e95de6..bdab508eb1f 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_scanfill.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -47,6 +48,7 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_font.h" +#include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lattice.h" #include "BKE_lib_id.h" @@ -55,6 +57,7 @@ #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_object.h" +#include "BKE_spline.hh" #include "BLI_sys_types.h" /* For #intptr_t support. */ @@ -745,10 +748,7 @@ static ModifierData *curve_get_tessellate_point(const Scene *scene, return pretessellatePoint; } -/** - * \return True if any modifier was applied. - */ -bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, +void BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *source_nurb, @@ -793,7 +793,6 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, const ModifierEvalContext mectx = {depsgraph, ob, apply_flag}; ModifierData *pretessellatePoint = curve_get_tessellate_point(scene, ob, for_render, editmode); - bool modified = false; if (pretessellatePoint) { VirtualModifierData virtualModifierData; @@ -813,7 +812,6 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, } mti->deformVerts(md, &mectx, nullptr, deformedVerts, numVerts); - modified = true; if (md == pretessellatePoint) { break; @@ -832,48 +830,59 @@ bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph, if (keyVerts) { MEM_freeN(keyVerts); } - return modified; } -static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[3] +/** + * \return True if the deformed curve control point data should be implicitly + * converted directly to a mesh, or false if it can be left as curve data via #CurveEval. + */ +static bool do_curve_implicit_mesh_conversion(const Curve *curve, + ModifierData *first_modifier, + const Scene *scene, + const ModifierMode required_mode) { - *r_vert_len = 0; + /* Skip implicit filling and conversion to mesh when using "fast text editing". */ + if (curve->flag & CU_FAST) { + return false; + } - LISTBASE_FOREACH (DispList *, dl, dispbase) { - *r_vert_len += (dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr; + /* Do implicit conversion to mesh with the object bevel mode. */ + if (curve->bevel_mode == CU_BEV_MODE_OBJECT && curve->bevobj != nullptr) { + return true; } - float(*allverts)[3] = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (*r_vert_len), __func__); - float *fp = (float *)allverts; - LISTBASE_FOREACH (DispList *, dl, dispbase) { - const int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); - memcpy(fp, dl->verts, sizeof(float) * ofs); - fp += ofs; + /* 2D curves are sometimes implicitly filled and converted to a mesh. */ + if (CU_DO_2DFILL(curve)) { + return true; } - return allverts; -} + /* Curve objects with implicit "tube" meshes should convert implicitly to a mesh. */ + if (curve->ext1 != 0.0f || curve->ext2 != 0.0f) { + return true; + } -static void displist_vert_coords_apply(ListBase *dispbase, const float (*allverts)[3]) -{ - const float *fp = (float *)allverts; - LISTBASE_FOREACH (DispList *, dl, dispbase) { - int ofs = 3 * ((dl->type == DL_INDEX3) ? dl->nr : dl->parts * dl->nr); - memcpy(dl->verts, fp, sizeof(float) * ofs); - fp += ofs; + /* If a non-geometry-nodes modifier is enabled before a nodes modifier, + * force conversion to mesh, since only the nodes modifier supports curve data. */ + ModifierData *md = first_modifier; + for (; md; md = md->next) { + if (BKE_modifier_is_enabled(scene, md, required_mode)) { + if (md->type == eModifierType_Nodes) { + break; + } + return true; + } } + + return false; } -static void curve_calc_modifiers_post(Depsgraph *depsgraph, - const Scene *scene, - Object *ob, - ListBase *dispbase, - const bool for_render, - const bool force_mesh_conversion, - Mesh **r_final) +static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, + const Scene *scene, + Object *ob, + const ListBase *dispbase, + const bool for_render) { const Curve *cu = (const Curve *)ob->data; - const bool editmode = (!for_render && (cu->editnurb || cu->editfont)); const bool use_cache = !for_render; @@ -897,166 +906,64 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) : pretessellatePoint->next; - if (r_final && *r_final) { - BKE_id_free(nullptr, *r_final); + GeometrySet geometry_set; + if (ob->type == OB_SURF || do_curve_implicit_mesh_conversion(cu, md, scene, required_mode)) { + Mesh *mesh = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); + geometry_set.replace_mesh(mesh); + } + else { + std::unique_ptr curve_eval = curve_eval_from_dna_curve( + *cu, ob->runtime.curve_cache->deformed_nurbs); + geometry_set.replace_curve(curve_eval.release()); } - Mesh *modified = nullptr; - float(*vertCos)[3] = nullptr; - int totvert = 0; for (; md; md = md->next) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - if (!BKE_modifier_is_enabled(scene, md, required_mode)) { continue; } - /* If we need normals, no choice, have to convert to mesh now. */ - const bool need_normal = mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md); - /* XXX 2.8 : now that batch cache is stored inside the ob->data - * we need to create a Mesh for each curve that uses modifiers. */ - if (modified == nullptr /* && need_normal */) { - if (vertCos != nullptr) { - displist_vert_coords_apply(dispbase, vertCos); - } - - if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, dispbase); - } + if (md->type == eModifierType_Nodes) { + mti->modifyGeometrySet(md, &mectx_apply, &geometry_set); + continue; + } - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); + if (!geometry_set.has_mesh()) { + geometry_set.replace_mesh(BKE_mesh_new_nomain(0, 0, 0, 0, 0)); } + Mesh *mesh = geometry_set.get_mesh_for_write(); - if (mti->type == eModifierTypeType_OnlyDeform || - (mti->type == eModifierTypeType_DeformOrConstruct && !modified)) { - if (modified) { - if (!vertCos) { - vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert); - } - if (need_normal) { - BKE_mesh_ensure_normals(modified); - } - mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert); - } - else { - if (!vertCos) { - vertCos = displist_vert_coords_alloc(dispbase, &totvert); - } - mti->deformVerts(md, &mectx_deform, nullptr, vertCos, totvert); + if (mti->type == eModifierTypeType_OnlyDeform) { + int totvert; + float(*vertex_coords)[3] = BKE_mesh_vert_coords_alloc(mesh, &totvert); + if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { + BKE_mesh_ensure_normals(mesh); } + mti->deformVerts(md, &mectx_deform, mesh, vertex_coords, totvert); + BKE_mesh_vert_coords_apply(mesh, vertex_coords); + MEM_freeN(vertex_coords); } else { - if (!r_final) { - /* makeDisplistCurveTypes could be used for beveling, where mesh - * is totally unnecessary, so we could stop modifiers applying - * when we found constructive modifier but mesh is unwanted. */ - break; - } - - if (modified) { - if (vertCos) { - Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( - nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); - BKE_id_free(nullptr, modified); - modified = temp_mesh; - - BKE_mesh_vert_coords_apply(modified, vertCos); - } - } - else { - if (vertCos) { - displist_vert_coords_apply(dispbase, vertCos); - } - - if (ELEM(ob->type, OB_CURVE, OB_FONT) && (cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, dispbase); - } - - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); - } - - if (vertCos) { - /* Vertex coordinates were applied to necessary data, could free it */ - MEM_freeN(vertCos); - vertCos = nullptr; - } - - if (need_normal) { - BKE_mesh_ensure_normals(modified); + if (mti->dependsOnNormals != nullptr && mti->dependsOnNormals(md)) { + BKE_mesh_ensure_normals(mesh); } - Mesh *mesh_applied = mti->modifyMesh(md, &mectx_apply, modified); - - if (mesh_applied) { - if (modified && modified != mesh_applied) { - BKE_id_free(nullptr, modified); - } - modified = mesh_applied; + Mesh *output_mesh = mti->modifyMesh(md, &mectx_apply, mesh); + if (mesh != output_mesh) { + geometry_set.replace_mesh(output_mesh); } } } - if (vertCos) { - if (modified) { - Mesh *temp_mesh = (Mesh *)BKE_id_copy_ex( - nullptr, &modified->id, nullptr, LIB_ID_COPY_LOCALIZE); - BKE_id_free(nullptr, modified); - modified = temp_mesh; + if (geometry_set.has_mesh()) { + Mesh *final_mesh = geometry_set.get_mesh_for_write(); - BKE_mesh_vert_coords_apply(modified, vertCos); - BKE_mesh_calc_normals(modified); + BKE_mesh_calc_normals(final_mesh); - MEM_freeN(vertCos); - } - else { - displist_vert_coords_apply(dispbase, vertCos); - MEM_freeN(vertCos); - vertCos = nullptr; - } + BLI_strncpy(final_mesh->id.name, cu->id.name, sizeof(final_mesh->id.name)); + *((short *)final_mesh->id.name) = ID_ME; } - if (r_final) { - if (force_mesh_conversion && !modified) { - /* XXX 2.8 : This is a workaround for by some deeper technical debts: - * - DRW Batch cache is stored inside the ob->data. - * - Curve data is not COWed for instances that use different modifiers. - * This can causes the modifiers to be applied on all user of the same data-block - * (see T71055) - * - * The easy workaround is to force to generate a Mesh that will be used for display data - * since a Mesh output is already used for generative modifiers. - * However it does not fix problems with actual edit data still being shared. - * - * The right solution would be to COW the Curve data block at the input of the modifier - * stack just like what the mesh modifier does. - */ - modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); - } - - if (modified) { - - /* XXX2.8(Sybren): make sure the face normals are recalculated as well */ - BKE_mesh_ensure_normals(modified); - - /* Special tweaks, needed since neither BKE_mesh_new_nomain_from_template() nor - * BKE_mesh_new_nomain_from_curve_displist() properly duplicate mat info... */ - BLI_strncpy(modified->id.name, cu->id.name, sizeof(modified->id.name)); - *((short *)modified->id.name) = ID_ME; - MEM_SAFE_FREE(modified->mat); - /* Set flag which makes it easier to see what's going on in a debugger. */ - modified->id.tag |= LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT; - modified->mat = (Material **)MEM_dupallocN(cu->mat); - modified->totcol = cu->totcol; - - (*r_final) = modified; - } - else { - (*r_final) = nullptr; - } - } - else if (modified != nullptr) { - /* Pretty stupid to generate that whole mesh if it's unused, yet we have to free it. */ - BKE_id_free(nullptr, modified); - } + return geometry_set; } static void displist_surf_indices(DispList *dl) @@ -1109,8 +1016,7 @@ static void evaluate_surface_object(Depsgraph *depsgraph, BKE_nurbList_duplicate(deformed_nurbs, &cu->nurb); } - bool force_mesh_conversion = BKE_curve_calc_modifiers_pre( - depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); + BKE_curve_calc_modifiers_pre(depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); LISTBASE_FOREACH (const Nurb *, nu, deformed_nurbs) { if (!(for_render || nu->hide == 0) || !BKE_nurb_check_valid_uv(nu)) { @@ -1173,8 +1079,14 @@ static void evaluate_surface_object(Depsgraph *depsgraph, } } - curve_calc_modifiers_post( - depsgraph, scene, ob, r_dispbase, for_render, force_mesh_conversion, r_final); + curve_to_filledpoly(cu, r_dispbase); + GeometrySet geometry_set = curve_calc_modifiers_post( + depsgraph, scene, ob, r_dispbase, for_render); + if (!geometry_set.has_mesh()) { + geometry_set.replace_mesh(BKE_mesh_new_nomain(0, 0, 0, 0, 0)); + } + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + *r_final = mesh_component.release(); } static void rotateBevelPiece(const Curve *cu, @@ -1394,12 +1306,11 @@ static void calc_bevfac_mapping(const Curve *cu, } } -static void evaluate_curve_type_object(Depsgraph *depsgraph, - const Scene *scene, - Object *ob, - const bool for_render, - ListBase *r_dispbase, - Mesh **r_final) +static GeometrySet evaluate_curve_type_object(Depsgraph *depsgraph, + const Scene *scene, + Object *ob, + const bool for_render, + ListBase *r_dispbase) { BLI_assert(ELEM(ob->type, OB_CURVE, OB_FONT)); const Curve *cu = (const Curve *)ob->data; @@ -1413,8 +1324,7 @@ static void evaluate_curve_type_object(Depsgraph *depsgraph, BKE_nurbList_duplicate(deformed_nurbs, BKE_curve_nurbs_get_for_read(cu)); } - bool force_mesh_conversion = BKE_curve_calc_modifiers_pre( - depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); + BKE_curve_calc_modifiers_pre(depsgraph, scene, ob, deformed_nurbs, deformed_nurbs, for_render); BKE_curve_bevelList_make(ob, deformed_nurbs, for_render); @@ -1603,16 +1513,8 @@ static void evaluate_curve_type_object(Depsgraph *depsgraph, BKE_displist_free(&dlbev); - if (!(cu->flag & CU_DEFORM_FILL)) { - curve_to_filledpoly(cu, r_dispbase); - } - - curve_calc_modifiers_post( - depsgraph, scene, ob, r_dispbase, for_render, force_mesh_conversion, r_final); - - if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { - curve_to_filledpoly(cu, r_dispbase); - } + curve_to_filledpoly(cu, r_dispbase); + return curve_calc_modifiers_post(depsgraph, scene, ob, r_dispbase, for_render); } void BKE_displist_make_curveTypes(Depsgraph *depsgraph, @@ -1621,25 +1523,43 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, const bool for_render) { BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)); + Curve &cow_curve = *(Curve *)ob->data; BKE_object_free_derived_caches(ob); + cow_curve.curve_eval = nullptr; - if (!ob->runtime.curve_cache) { - ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); - } - + ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); ListBase *dispbase = &(ob->runtime.curve_cache->disp); - Mesh *mesh_eval = nullptr; if (ob->type == OB_SURF) { + Mesh *mesh_eval; evaluate_surface_object(depsgraph, scene, ob, for_render, dispbase, &mesh_eval); + BKE_object_eval_assign_data(ob, &mesh_eval->id, true); } else { - evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase, &mesh_eval); - } + GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase); + + if (geometry.has_curve()) { + /* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval + * added to the curve here, it will also contain a copy of the original curve's data. This is + * essential, because it maintains the expected behavior for evaluated curve data from before + * the CurveEval data type was introduced, when an evaluated object's curve data was just a + * copy of the original curve and everything else ended up in #CurveCache. */ + CurveComponent &curve_component = geometry.get_component_for_write(); + cow_curve.curve_eval = curve_component.get_for_write(); + BKE_object_eval_assign_data(ob, &cow_curve.id, false); + } + else if (geometry.has_mesh()) { + /* Most areas of Blender don't yet know how to look in #geometry_set_eval for evaluated mesh + * data, and look in #data_eval instead. When the object evaluates to a curve, that field + * must be used for the evaluated curve data, but otherwise we can use the field to store a + * pointer to the mesh, so more areas can retrieve the mesh. */ + MeshComponent &mesh_component = geometry.get_component_for_write(); + Mesh *mesh_eval = mesh_component.get_for_write(); + BKE_object_eval_assign_data(ob, &mesh_eval->id, false); + } - if (mesh_eval != nullptr) { - BKE_object_eval_assign_data(ob, &mesh_eval->id, true); + ob->runtime.geometry_set_eval = new GeometrySet(std::move(geometry)); } boundbox_displist_object(ob); @@ -1656,7 +1576,9 @@ void BKE_displist_make_curveTypes_forRender( evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final); } else { - evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase, r_final); + GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase); + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + *r_final = mesh_component.release(); } } diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index a24b60ee288..afafd766760 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -64,6 +64,8 @@ void CurveComponent::clear() delete curve_; } if (curve_for_render_ != nullptr) { + /* The curve created by this component should not have any edit mode data. */ + BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); BKE_id_free(nullptr, curve_for_render_); curve_for_render_ = nullptr; } diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index dafebef1812..e717d289894 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -408,7 +408,7 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) if (ob->type == OB_VOLUME) { return has_mesh || has_pointcloud || has_curve; } - if (ob->type == OB_CURVE) { + if (ELEM(ob->type, OB_CURVE, OB_FONT)) { return has_mesh || has_pointcloud || has_volume; } return false; diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 0e19324a3c1..9dca2c2907e 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -51,16 +51,6 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS } } -static void add_curve_data_as_geometry_component(const Object &object, GeometrySet &geometry_set) -{ - BLI_assert(object.type == OB_CURVE); - if (object.data != nullptr) { - std::unique_ptr curve = curve_eval_from_dna_curve(*(const Curve *)object.data); - CurveComponent &curve_component = geometry_set.get_component_for_write(); - curve_component.replace(curve.release(), GeometryOwnershipType::Owned); - } -} - /** * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. */ @@ -84,9 +74,6 @@ static GeometrySet object_get_geometry_set_for_read(const Object &object) if (object.type == OB_MESH) { add_final_mesh_as_geometry_component(object, geometry_set); } - else if (object.type == OB_CURVE) { - add_curve_data_as_geometry_component(object, geometry_set); - } /* TODO: Cover the case of point-clouds without modifiers-- they may not be covered by the * #geometry_set_eval case above. */ diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index d5524312612..9fd75be0d35 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -483,8 +483,24 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, return 0; } +/** + * Copy evaluated texture space from curve to mesh. + * + * \note We disable auto texture space feature since that will cause texture space to evaluate + * differently for curve and mesh, since curves use control points and handles to calculate the + * bounding box, and mesh uses the tessellated curve. + */ +static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me) +{ + me->texflag = cu->texflag & ~CU_AUTOSPACE; + copy_v3_v3(me->loc, cu->loc); + copy_v3_v3(me->size, cu->size); + BKE_mesh_texspace_calc(me); +} + Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase) { + const Curve *cu = ob->data; Mesh *mesh; MVert *allvert; MEdge *alledge; @@ -493,7 +509,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * MLoopUV *alluv = NULL; int totvert, totedge, totloop, totpoly; - if (mesh_nurbs_displist_to_mdata(ob->data, + if (mesh_nurbs_displist_to_mdata(cu, dispbase, &allvert, &totvert, @@ -529,6 +545,12 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname); } + mesh_copy_texture_space_from_curve_type(cu, mesh); + + /* Copy curve materials. */ + mesh->mat = (Material **)MEM_dupallocN(cu->mat); + mesh->totcol = cu->totcol; + MEM_freeN(allvert); MEM_freeN(alledge); MEM_freeN(allloop); @@ -612,17 +634,7 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char me->totcol = cu->totcol; me->mat = cu->mat; - /* Copy evaluated texture space from curve to mesh. - * - * Note that we disable auto texture space feature since that will cause - * texture space to evaluate differently for curve and mesh, since curve - * uses CV to calculate bounding box, and mesh uses what is coming from - * tessellated curve. - */ - me->texflag = cu->texflag & ~CU_AUTOSPACE; - copy_v3_v3(me->loc, cu->loc); - copy_v3_v3(me->size, cu->size); - BKE_mesh_texspace_calc(me); + mesh_copy_texture_space_from_curve_type(cu, me); cu->mat = NULL; cu->totcol = 0; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index b4343578eab..465ec9dc665 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1341,6 +1341,11 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti = BKE_modifier_get_info(modifier_type); + /* Surface and lattice objects don't output geometry sets. */ + if (mti->modifyGeometrySet != NULL && ELEM(ob->type, OB_SURF, OB_LATTICE)) { + return false; + } + /* Only geometry objects should be able to get modifiers T25291. */ if (ob->type == OB_HAIR) { return (mti->modifyHair != NULL) || (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly); diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 47d4d03d1e1..04739ec19d3 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -855,7 +855,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, dupli->ob_data = (ID *)volume; } } - if (ctx->object->type != OB_CURVE || geometry_set_is_instance) { + if (!ELEM(ctx->object->type, OB_CURVE, OB_FONT) || geometry_set_is_instance) { const CurveComponent *curve_component = geometry_set.get_component_for_read(); if (curve_component != nullptr) { const Curve *curve = curve_component->get_curve_for_render(); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 30ec9e948fd..1081528ece1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -98,7 +98,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) object->runtime = runtime; object->runtime.data_orig = data_orig; object->runtime.bb = bb; - if (ELEM(object->type, OB_MESH, OB_LATTICE) && data_eval != nullptr) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVE, OB_FONT) && data_eval != nullptr) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* If geometry is tagged for update it means, that part of * evaluated mesh are not valid anymore. In this case we can not @@ -112,9 +112,11 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) BKE_object_free_derived_caches(object); } else { - /* Do same thing as object update: override actual object data - * pointer with evaluated datablock. */ - object->data = data_eval; + /* Do same thing as object update: override actual object data pointer with evaluated + * datablock, but only if the evaluated data has the same type as the original data. */ + if (GS(((ID *)object->data)->name) == GS(data_eval->name)) { + object->data = data_eval; + } /* Evaluated mesh simply copied edit_mesh pointer from * original mesh during update, need to make sure no dead diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c index fd68b319f02..5356700f156 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_text.c +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -180,19 +180,12 @@ static void edit_text_cache_populate_boxes(OVERLAY_Data *vedata, Object *ob) void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_PrivateData *pd = vedata->stl->pd; - Curve *cu = ob->data; struct GPUBatch *geom; bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; - bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || cu->ext2 != 0.0f; - if ((cu->flag & CU_FAST) || !has_surface) { - geom = DRW_cache_text_edge_wire_get(ob); - if (geom) { - DRW_shgroup_call(pd->edit_text_wire_grp[do_in_front], geom, ob); - } - } - else { - /* object mode draws */ + geom = DRW_cache_text_edge_wire_get(ob); + if (geom) { + DRW_shgroup_call(pd->edit_text_wire_grp[do_in_front], geom, ob); } edit_text_cache_populate_select(vedata, ob); diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index b8a61ecc403..fde376beeb2 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -218,18 +218,10 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, struct GPUBatch *geom = NULL; switch (ob->type) { case OB_CURVE: - if (!pd->wireframe_mode && !use_wire && ob->runtime.curve_cache && - BKE_displist_has_faces(&ob->runtime.curve_cache->disp)) { - break; - } geom = DRW_cache_curve_edge_wire_get(ob); break; case OB_FONT: - if (!pd->wireframe_mode && !use_wire && ob->runtime.curve_cache && - BKE_displist_has_faces(&ob->runtime.curve_cache->disp)) { - break; - } - geom = DRW_cache_text_loose_edges_get(ob); + geom = DRW_cache_text_edge_wire_get(ob); break; case OB_SURF: geom = DRW_cache_surf_edge_wire_get(ob); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 000ab540813..a3a5d6b065a 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -794,6 +794,10 @@ GPUBatch *DRW_gpencil_dummy_buffer_get(void) /* -------------------------------------------------------------------- */ /** \name Common Object API + * + * \note Curve and text objects evaluate to the evaluated geometry set's mesh component if + * they have a surface, so curve objects themselves do not have a surface (the mesh component + * is presented to render engines as a separate object). * \{ */ GPUBatch *DRW_cache_object_all_edges_get(Object *ob) @@ -814,11 +818,11 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) case OB_MESH: return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold); case OB_CURVE: - return DRW_cache_curve_edge_detection_get(ob, r_is_manifold); + return NULL; case OB_SURF: return DRW_cache_surf_edge_detection_get(ob, r_is_manifold); case OB_FONT: - return DRW_cache_text_edge_detection_get(ob, r_is_manifold); + return NULL; case OB_MBALL: return DRW_cache_mball_edge_detection_get(ob, r_is_manifold); case OB_HAIR: @@ -838,11 +842,11 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) case OB_MESH: return DRW_cache_mesh_face_wireframe_get(ob); case OB_CURVE: - return DRW_cache_curve_face_wireframe_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_face_wireframe_get(ob); case OB_FONT: - return DRW_cache_text_face_wireframe_get(ob); + return NULL; case OB_MBALL: return DRW_cache_mball_face_wireframe_get(ob); case OB_HAIR: @@ -865,11 +869,11 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) case OB_MESH: return DRW_cache_mesh_loose_edges_get(ob); case OB_CURVE: - return DRW_cache_curve_loose_edges_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_loose_edges_get(ob); case OB_FONT: - return DRW_cache_text_loose_edges_get(ob); + return NULL; case OB_MBALL: return NULL; case OB_HAIR: @@ -889,11 +893,11 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) case OB_MESH: return DRW_cache_mesh_surface_get(ob); case OB_CURVE: - return DRW_cache_curve_surface_get(ob); + return NULL; case OB_SURF: return DRW_cache_surf_surface_get(ob); case OB_FONT: - return DRW_cache_text_surface_get(ob); + return NULL; case OB_MBALL: return DRW_cache_mball_surface_get(ob); case OB_HAIR: @@ -939,9 +943,9 @@ int DRW_cache_object_material_count_get(struct Object *ob) Mesh *me = BKE_object_get_evaluated_mesh(ob); if (me != NULL && type != OB_POINTCLOUD) { - /* Some object types (e.g. curves) can have a Curve in ob->data, but will be rendered as mesh. - * For point clouds this never happens. Ideally this check would happen at another level and we - * would just have to care about ob->data here. */ + /* Some object types can have one data type in ob->data, but will be rendered as mesh. + * For point clouds this never happens. Ideally this check would happen at another level + * and we would just have to care about ob->data here. */ type = OB_MESH; } @@ -974,11 +978,11 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, case OB_MESH: return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_CURVE: - return DRW_cache_curve_surface_shaded_get(ob, gpumat_array, gpumat_array_len); + return NULL; case OB_SURF: return DRW_cache_surf_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_FONT: - return DRW_cache_text_surface_shaded_get(ob, gpumat_array, gpumat_array_len); + return NULL; case OB_MBALL: return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len); case OB_HAIR: @@ -2929,20 +2933,13 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob) GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob) { BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - return DRW_curve_batch_cache_get_wire_edge(cu); } GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob) { BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_normal_edge(cu); } @@ -2963,75 +2960,6 @@ GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob) return DRW_curve_batch_cache_get_edit_verts(cu); } -GPUBatch *DRW_cache_curve_surface_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface(mesh_eval); - } - - return DRW_curve_batch_cache_get_triangles_with_normals(cu); -} - -GPUBatch *DRW_cache_curve_loose_edges_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - - /* TODO */ - UNUSED_VARS(cu); - return NULL; -} - -GPUBatch *DRW_cache_curve_face_wireframe_get(Object *ob) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_wireframes_face(mesh_eval); - } - - return DRW_curve_batch_cache_get_wireframes_face(cu); -} - -GPUBatch *DRW_cache_curve_edge_detection_get(Object *ob, bool *r_is_manifold) -{ - BLI_assert(ob->type == OB_CURVE); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_edge_detection(mesh_eval, r_is_manifold); - } - - return DRW_curve_batch_cache_get_edge_detection(cu, r_is_manifold); -} - -/* Return list of batches */ -GPUBatch **DRW_cache_curve_surface_shaded_get(Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len) -{ - BLI_assert(ob->type == OB_CURVE); - - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); - } - - return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3075,96 +3003,9 @@ GPUBatch *DRW_cache_text_edge_wire_get(Object *ob) { BLI_assert(ob->type == OB_FONT); struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - const bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || - cu->ext2 != 0.0f; - if (!has_surface) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - - return DRW_curve_batch_cache_get_wire_edge(cu); -} - -GPUBatch *DRW_cache_text_surface_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface(mesh_eval); - } - - return DRW_curve_batch_cache_get_triangles_with_normals(cu); -} - -GPUBatch *DRW_cache_text_edge_detection_get(Object *ob, bool *r_is_manifold) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_edge_detection(mesh_eval, r_is_manifold); - } - - return DRW_curve_batch_cache_get_edge_detection(cu, r_is_manifold); -} - -GPUBatch *DRW_cache_text_loose_edges_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_loose_edges(mesh_eval); - } - return DRW_curve_batch_cache_get_wire_edge(cu); } -GPUBatch *DRW_cache_text_face_wireframe_get(Object *ob) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_wireframes_face(mesh_eval); - } - - return DRW_curve_batch_cache_get_wireframes_face(cu); -} - -GPUBatch **DRW_cache_text_surface_shaded_get(Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len) -{ - BLI_assert(ob->type == OB_FONT); - struct Curve *cu = ob->data; - struct Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (cu->editfont && (cu->flag & CU_FAST)) { - return NULL; - } - if (mesh_eval != NULL) { - return DRW_mesh_batch_cache_get_surface_shaded(mesh_eval, gpumat_array, gpumat_array_len); - } - - return DRW_curve_batch_cache_get_surface_shaded(cu, gpumat_array, gpumat_array_len); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3544,6 +3385,8 @@ void drw_batch_cache_validate(Object *ob) break; case OB_CURVE: case OB_FONT: + DRW_curve_batch_cache_validate((Curve *)ob->data); + break; case OB_SURF: if (mesh_eval != NULL) { DRW_mesh_batch_cache_validate(mesh_eval); @@ -3592,6 +3435,8 @@ void drw_batch_cache_generate_requested(Object *ob) break; case OB_CURVE: case OB_FONT: + DRW_curve_batch_cache_create_requested(ob, scene); + break; case OB_SURF: if (mesh_eval) { DRW_mesh_batch_cache_create_requested( @@ -3618,8 +3463,6 @@ void DRW_batch_cache_free_old(Object *ob, int ctime) case OB_MESH: DRW_mesh_batch_cache_free_old((Mesh *)ob->data, ctime); break; - case OB_CURVE: - case OB_FONT: case OB_SURF: if (mesh_eval) { DRW_mesh_batch_cache_free_old(mesh_eval, ctime); diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 6b2b0a173fe..5863ada2ccf 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -154,28 +154,14 @@ struct GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(struct Object *ob); struct GPUBatch *DRW_cache_mesh_face_wireframe_get(struct Object *ob); /* Curve */ -struct GPUBatch *DRW_cache_curve_surface_get(struct Object *ob); -struct GPUBatch **DRW_cache_curve_surface_shaded_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len); -struct GPUBatch *DRW_cache_curve_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_wire_get(struct Object *ob); -struct GPUBatch *DRW_cache_curve_face_wireframe_get(struct Object *ob); -struct GPUBatch *DRW_cache_curve_edge_detection_get(struct Object *ob, bool *r_is_manifold); /* edit-mode */ struct GPUBatch *DRW_cache_curve_edge_normal_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_edge_overlay_get(struct Object *ob); struct GPUBatch *DRW_cache_curve_vert_overlay_get(struct Object *ob); /* Font */ -struct GPUBatch *DRW_cache_text_surface_get(struct Object *ob); -struct GPUBatch *DRW_cache_text_edge_detection_get(struct Object *ob, bool *r_is_manifold); -struct GPUBatch *DRW_cache_text_loose_edges_get(struct Object *ob); struct GPUBatch *DRW_cache_text_edge_wire_get(struct Object *ob); -struct GPUBatch **DRW_cache_text_surface_shaded_get(struct Object *ob, - struct GPUMaterial **gpumat_array, - uint gpumat_array_len); -struct GPUBatch *DRW_cache_text_face_wireframe_get(struct Object *ob); /* Surface */ struct GPUBatch *DRW_cache_surf_surface_get(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 1efe0c080be..0804745fab5 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -112,43 +112,6 @@ static void curve_render_overlay_verts_edges_len_get(ListBase *lb, } } -static void curve_render_wire_verts_edges_len_get(const CurveCache *ob_curve_cache, - int *r_curve_len, - int *r_vert_len, - int *r_edge_len) -{ - BLI_assert(r_vert_len || r_edge_len); - int vert_len = 0; - int edge_len = 0; - int curve_len = 0; - LISTBASE_FOREACH (const BevList *, bl, &ob_curve_cache->bev) { - if (bl->nr > 0) { - const bool is_cyclic = bl->poly != -1; - edge_len += (is_cyclic) ? bl->nr : bl->nr - 1; - vert_len += bl->nr; - curve_len += 1; - } - } - LISTBASE_FOREACH (const DispList *, dl, &ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - BLI_assert(dl->parts == 1); - const bool is_cyclic = dl->type == DL_POLY; - edge_len += (is_cyclic) ? dl->nr : dl->nr - 1; - vert_len += dl->nr; - curve_len += 1; - } - } - if (r_vert_len) { - *r_vert_len = vert_len; - } - if (r_edge_len) { - *r_edge_len = edge_len; - } - if (r_curve_len) { - *r_curve_len = curve_len; - } -} - static void curve_eval_render_wire_verts_edges_len_get(const CurveEval &curve_eval, int *r_curve_len, int *r_vert_len, @@ -243,7 +206,7 @@ enum { }; /* - * ob_curve_cache can be NULL, only needed for CU_DATATYPE_WIRE + * ob_curve_cache can be NULL */ static CurveRenderData *curve_render_data_create(Curve *cu, CurveCache *ob_curve_cache, @@ -267,12 +230,6 @@ static CurveRenderData *curve_render_data_create(Curve *cu, &rdata->wire.vert_len, &rdata->wire.edge_len); } - else { - curve_render_wire_verts_edges_len_get(rdata->ob_curve_cache, - &rdata->wire.curve_len, - &rdata->wire.vert_len, - &rdata->wire.edge_len); - } } if (cu->editnurb) { @@ -594,6 +551,10 @@ void DRW_curve_batch_cache_free(Curve *cu) /* GPUBatch cache usage. */ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curves_pos) { + if (rdata->curve_eval == nullptr) { + return; + } + static GPUVertFormat format = {0}; static struct { uint pos; @@ -606,46 +567,26 @@ static void curve_create_curves_pos(CurveRenderData *rdata, GPUVertBuf *vbo_curv GPU_vertbuf_init_with_format(vbo_curves_pos, &format); GPU_vertbuf_data_alloc(vbo_curves_pos, vert_len); - if (rdata->curve_eval != nullptr) { - const CurveEval &curve_eval = *rdata->curve_eval; - Span splines = curve_eval.splines(); - Array offsets = curve_eval.evaluated_point_offsets(); - BLI_assert(offsets.last() == vert_len); - - for (const int i_spline : splines.index_range()) { - Span positions = splines[i_spline]->evaluated_positions(); - for (const int i_point : positions.index_range()) { - GPU_vertbuf_attr_set( - vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]); - } - } - } - else { - BLI_assert(rdata->ob_curve_cache != nullptr); - - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const int i_end = v_idx + bl->nr; - for (const BevPoint *bevp = bl->bevpoints; v_idx < i_end; v_idx++, bevp++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, bevp->vec); - } - } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - for (int i = 0; i < dl->nr; v_idx++, i++) { - GPU_vertbuf_attr_set(vbo_curves_pos, attr_id.pos, v_idx, &((float(*)[3])dl->verts)[i]); - } - } + const CurveEval &curve_eval = *rdata->curve_eval; + Span splines = curve_eval.splines(); + Array offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); + + for (const int i_spline : splines.index_range()) { + Span positions = splines[i_spline]->evaluated_positions(); + for (const int i_point : positions.index_range()) { + GPU_vertbuf_attr_set( + vbo_curves_pos, attr_id.pos, offsets[i_spline] + i_point, positions[i_point]); } - BLI_assert(v_idx == vert_len); } } static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_curve_lines) { + if (rdata->curve_eval == nullptr) { + return; + } + const int vert_len = curve_render_data_wire_verts_len_get(rdata); const int edge_len = curve_render_data_wire_edges_len_get(rdata); const int curve_len = curve_render_data_wire_curve_len_get(rdata); @@ -655,54 +596,20 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c GPUIndexBufBuilder elb; GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, index_len, vert_len); - if (rdata->curve_eval != nullptr) { - const CurveEval &curve_eval = *rdata->curve_eval; - Span splines = curve_eval.splines(); - Array offsets = curve_eval.evaluated_point_offsets(); - BLI_assert(offsets.last() == vert_len); - - for (const int i_spline : splines.index_range()) { - const int eval_size = splines[i_spline]->evaluated_points_size(); - if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) { - GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); - } - for (const int i_point : IndexRange(eval_size)) { - GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point); - } - GPU_indexbuf_add_primitive_restart(&elb); - } - } - else { - BLI_assert(rdata->ob_curve_cache != nullptr); + const CurveEval &curve_eval = *rdata->curve_eval; + Span splines = curve_eval.splines(); + Array offsets = curve_eval.evaluated_point_offsets(); + BLI_assert(offsets.last() == vert_len); - int v_idx = 0; - LISTBASE_FOREACH (const BevList *, bl, &rdata->ob_curve_cache->bev) { - if (bl->nr <= 0) { - continue; - } - const bool is_cyclic = bl->poly != -1; - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (bl->nr - 1)); - } - for (int i = 0; i < bl->nr; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + i); - } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += bl->nr; + for (const int i_spline : splines.index_range()) { + const int eval_size = splines[i_spline]->evaluated_points_size(); + if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); } - LISTBASE_FOREACH (const DispList *, dl, &rdata->ob_curve_cache->disp) { - if (ELEM(dl->type, DL_SEGM, DL_POLY)) { - const bool is_cyclic = dl->type == DL_POLY; - if (is_cyclic) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + (dl->nr - 1)); - } - for (int i = 0; i < dl->nr; i++) { - GPU_indexbuf_add_generic_vert(&elb, v_idx + i); - } - GPU_indexbuf_add_primitive_restart(&elb); - v_idx += dl->nr; - } + for (const int i_point : IndexRange(eval_size)) { + GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + i_point); } + GPU_indexbuf_add_primitive_restart(&elb); } GPU_indexbuf_build_in_place(&elb, ibo_curve_lines); diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index d1fe162fc4a..19aad20cbc1 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -535,7 +535,6 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) newob = true; cu = (Curve *)obedit->data; - cu->flag |= CU_DEFORM_FILL; if (type & CU_PRIM_PATH) { cu->flag |= CU_PATH | CU_3D; diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index fcc92345bea..d503297f540 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -270,7 +270,7 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, return nullptr; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE, OB_FONT)) { return nullptr; } diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index 688ba23333a..d2ec7fb84db 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -94,7 +94,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele { Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); - cu->flag |= CU_DEFORM_FILL | CU_3D; + cu->flag |= CU_3D; cu->actvert = CU_ACT_NONE; cu->resolu = 1; diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index 31ecf27cf7e..7410757a9d9 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -46,7 +46,7 @@ void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTim { curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); - curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc index 9b30b524729..d6977d9c91a 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.cc +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -62,7 +62,7 @@ void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime { curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); - curve_->flag |= CU_DEFORM_FILL | CU_3D; + curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 520fc6c1b00..77ab2b67f3d 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -302,8 +302,11 @@ typedef struct Curve { float fsize_realtime; /** - * A pointer to curve data from geometry nodes, currently only set for evaluated - * objects by the dependency graph iterator, and owned by #geometry_set_eval. + * A pointer to curve data from evaluation. Owned by the object's #geometry_set_eval, either as a + * geometry instance or the data of the evalauted #CurveComponent. The curve may also contain + * data in the #nurb list, but for evaluated curves this is the proper place to retrieve data, + * since it also contains the result of geometry nodes evaluation, and isn't just a copy of the + * original object data. */ struct CurveEval *curve_eval; @@ -344,8 +347,7 @@ enum { CU_DS_EXPAND = 1 << 11, /** make use of the path radius if this is enabled (default for new curves) */ CU_PATH_RADIUS = 1 << 12, - /** fill 2d curve after deformation */ - CU_DEFORM_FILL = 1 << 13, + /* CU_DEFORM_FILL = 1 << 13, */ /* DEPRECATED */ /** fill bevel caps */ CU_FILL_CAPS = 1 << 14, /** map taper object to beveled area */ diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 0250d853898..5a88ce7c9f5 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -158,8 +158,7 @@ typedef struct Object_Runtime { struct ID *data_orig; /** * Object data structure created during object evaluation. It has all modifiers applied. - * The type is determined by the type of the original object. For example, for mesh and curve - * objects, this is a mesh. For a volume object, this is a volume. + * The type is determined by the type of the original object. */ struct ID *data_eval; diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 9c6659a7130..0bfb1200f49 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -1809,12 +1809,6 @@ static void rna_def_curve(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Twist Smooth", "Smoothing iteration for tangents"); RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - prop = RNA_def_property(srna, "use_fill_deform", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_DEFORM_FILL); - RNA_def_property_ui_text( - prop, "Fill Deformed", "Fill curve after applying shape keys and all modifiers"); - RNA_def_property_update(prop, 0, "rna_Curve_update_data"); - prop = RNA_def_property(srna, "use_fill_caps", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_FILL_CAPS); RNA_def_property_ui_text(prop, "Fill Caps", "Fill caps for beveled curves"); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f6d145bfdd7..3b952e1e649 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1075,9 +1075,10 @@ ModifierTypeInfo modifierType_Nodes = { /* srna */ &RNA_NodesModifier, /* type */ eModifierTypeType_Constructive, /* flags */ - static_cast( - eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode | - eModifierTypeFlag_EnableInEditmode | eModifierTypeFlag_SupportsMapping), + static_cast(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_SupportsEditmode | + eModifierTypeFlag_EnableInEditmode | + eModifierTypeFlag_SupportsMapping), /* icon */ ICON_NODETREE, /* copyData */ copyData, -- cgit v1.2.3 From 15405685d9a0111588a769fdba98e3d34464513f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sat, 11 Sep 2021 15:00:36 -0500 Subject: Cleanup: Remove no-op/unused code --- source/blender/blenkernel/BKE_displist.h | 5 --- source/blender/blenkernel/intern/displist.cc | 60 ++++++++-------------------- 2 files changed, 17 insertions(+), 48 deletions(-) diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 37e144ebbd3..db5663fcc94 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -82,7 +82,6 @@ DispList *BKE_displist_find(struct ListBase *lb, int type); void BKE_displist_normals_add(struct ListBase *lb); void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri); void BKE_displist_free(struct ListBase *lb); -bool BKE_displist_has_faces(const struct ListBase *lb); void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph, const struct Scene *scene, @@ -94,10 +93,6 @@ void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph, struct ListBase *dispbase, struct Mesh **r_final); void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); -void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - struct ListBase *dispbase); void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, const struct Scene *scene, diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index bdab508eb1f..f37978d14bb 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -104,17 +104,6 @@ DispList *BKE_displist_find(ListBase *lb, int type) return nullptr; } -bool BKE_displist_has_faces(const ListBase *lb) -{ - LISTBASE_FOREACH (const DispList *, dl, lb) { - if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) { - return true; - } - } - - return false; -} - void BKE_displist_copy(ListBase *lbn, const ListBase *lb) { BKE_displist_free(lbn); @@ -691,23 +680,9 @@ void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_mball_texspace_calc(ob); object_deform_mball(ob, &ob->runtime.curve_cache->disp); - - /* No-op for MBALLs anyway... */ - boundbox_displist_object(ob); } } -void BKE_displist_make_mball_forRender(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - ListBase *dispbase) -{ - BKE_mball_polygonize(depsgraph, scene, ob, dispbase); - BKE_mball_texspace_calc(ob); - - object_deform_mball(ob, dispbase); -} - static ModifierData *curve_get_tessellate_point(const Scene *scene, const Object *ob, const bool for_render, @@ -1606,27 +1581,26 @@ void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3]) /* this is confusing, there's also min_max_object, applying the obmat... */ static void boundbox_displist_object(Object *ob) { - if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { - /* Curve's BB is already calculated as a part of modifier stack, - * here we only calculate object BB based on final display list. */ + BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)); + /* Curve's BB is already calculated as a part of modifier stack, + * here we only calculate object BB based on final display list. */ - /* object's BB is calculated from final displist */ - if (ob->runtime.bb == nullptr) { - ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__); - } + /* object's BB is calculated from final displist */ + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), __func__); + } - const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); - if (mesh_eval) { - BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); - } - else { - float min[3], max[3]; + const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob); + if (mesh_eval) { + BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); + } + else { + float min[3], max[3]; - INIT_MINMAX(min, max); - BKE_displist_minmax(&ob->runtime.curve_cache->disp, min, max); - BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); + INIT_MINMAX(min, max); + BKE_displist_minmax(&ob->runtime.curve_cache->disp, min, max); + BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); - ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; - } + ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY; } } -- cgit v1.2.3 From 2aa7edbe6b4428d8f7915a9dc402d1209a4b114b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 12 Sep 2021 19:51:16 +1000 Subject: Cleanup: spelling --- intern/ghost/intern/GHOST_XrContext.cpp | 2 +- source/blender/blenkernel/BKE_anonymous_attribute.hh | 2 +- source/blender/blenloader/intern/versioning_300.c | 4 ++-- source/blender/editors/space_action/action_edit.c | 4 ++-- source/blender/functions/FN_field.hh | 2 +- source/blender/makesdna/DNA_curve_types.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 3584f4b8e90..fe8fec052fe 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -479,7 +479,7 @@ GHOST_TXrGraphicsBinding GHOST_XrContext::determineGraphicsBindingTypeToUse( /* Return the first working type. */ for (GHOST_TXrGraphicsBinding type : enabled_types) { #ifdef WIN32 - /* The SteamVR OpenGL backend currently fails for NVIDIA gpus. Disable it and allow falling + /* The SteamVR OpenGL backend currently fails for NVIDIA GPU's. Disable it and allow falling * back to the DirectX one. */ if ((m_runtime_id == OPENXR_RUNTIME_STEAMVR) && (type == GHOST_kXrGraphicsOpenGL) && ((create_info->context_flag & GHOST_kXrContextGpuNVIDIA) != 0)) { diff --git a/source/blender/blenkernel/BKE_anonymous_attribute.hh b/source/blender/blenkernel/BKE_anonymous_attribute.hh index 201fa2b2f52..56e6335c7c0 100644 --- a/source/blender/blenkernel/BKE_anonymous_attribute.hh +++ b/source/blender/blenkernel/BKE_anonymous_attribute.hh @@ -120,7 +120,7 @@ template class OwnedAnonymousAttributeID { return BKE_anonymous_attribute_id_has_strong_references(data_); } - /** Extract the onwership of the currently wrapped anonymous id. */ + /** Extract the ownership of the currently wrapped anonymous id. */ const AnonymousAttributeID *extract() { const AnonymousAttributeID *extracted_data = data_; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index b474209e618..6b8f011e9fc 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -685,7 +685,7 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_COLLECTION_INFO: return false; - /* Maybe legacy: Transfered *all* attributes before, will not transfer all built-ins now. */ + /* Maybe legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ case GEO_NODE_CURVE_ENDPOINTS: case GEO_NODE_CURVE_TO_POINTS: return false; @@ -732,7 +732,7 @@ static bool geometry_node_is_293_legacy(const short node_type) return true; /* Legacy: More complex attribute inputs or outputs. */ - case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain dropdown. */ + case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 6f4e295cbb2..3e38be243c9 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -192,7 +192,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const bGPDlayer *gpl = ale->data; bGPDframe *gpf; - /* find gp-frame which is less than or equal to cframe */ + /* Find gp-frame which is less than or equal to current-frame. */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { const float framenum = (float)gpf->framenum; *min = min_ff(*min, framenum); @@ -204,7 +204,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const MaskLayer *masklay = ale->data; MaskLayerShape *masklay_shape; - /* find mask layer which is less than or equal to cframe */ + /* Find mask layer which is less than or equal to current-frame. */ for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { const float framenum = (float)masklay_shape->frame; diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 976d260d91b..730a8046646 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -37,7 +37,7 @@ * use is to compose multiple existing fields into new fields. * * When fields are evaluated, they are converted into a multi-function procedure which allows - * efficient compution. In the future, we might support different field evaluation mechanisms for + * efficient computation. In the future, we might support different field evaluation mechanisms for * e.g. the following scenarios: * - Latency of a single evaluation is more important than throughput. * - Evaluation should happen on other hardware like GPUs. diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 77ab2b67f3d..a2433dbbbbd 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -303,7 +303,7 @@ typedef struct Curve { /** * A pointer to curve data from evaluation. Owned by the object's #geometry_set_eval, either as a - * geometry instance or the data of the evalauted #CurveComponent. The curve may also contain + * geometry instance or the data of the evaluated #CurveComponent. The curve may also contain * data in the #nurb list, but for evaluated curves this is the proper place to retrieve data, * since it also contains the result of geometry nodes evaluation, and isn't just a copy of the * original object data. -- cgit v1.2.3 From b5a1c194c5dde20b13e3dd3372055d8b5e58a18e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 12 Sep 2021 19:51:17 +1000 Subject: Cleanup: early return from smoothview when the view is unchanged --- source/blender/editors/space_view3d/view3d_view.c | 156 ++++++++++------------ 1 file changed, 71 insertions(+), 85 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index a6dd95cac04..f5da7c14a88 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -199,105 +199,91 @@ void ED_view3d_smooth_view_ex( sms.to_camera = true; /* restore view3d values in end */ } - bool changed = false; /* zero means no difference */ - - if (sview->camera_old != sview->camera) { - changed = true; - } - else if (sms.dst.dist != rv3d->dist) { - changed = true; - } - else if (sms.dst.lens != v3d->lens) { - changed = true; - } - else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs)) { - changed = true; - } - else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat)) { - changed = true; + if ((sview->camera_old == sview->camera) && /* Camera. */ + (sms.dst.dist == rv3d->dist) && /* Distance. */ + (sms.dst.lens == v3d->lens) && /* Lens. */ + equals_v3v3(sms.dst.ofs, rv3d->ofs) && /* Offset. */ + equals_v4v4(sms.dst.quat, rv3d->viewquat) /* Rotation. */ + ) { + /* Early return if nothing changed. */ + return; } - /* The new view is different from the previous state. */ - if (changed) { - - /* Skip smooth viewing for external render engine draw. */ - if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { + /* Skip smooth viewing for external render engine draw. */ + if (smooth_viewtx && !(v3d->shading.type == OB_RENDER && rv3d->render_engine)) { - /* original values */ - if (sview->camera_old) { - Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); - if (sview->ofs != NULL) { - sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); - } - ED_view3d_from_object( - ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); - } - /* grid draw as floor */ - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - /* use existing if exists, means multiple calls to smooth view - * won't lose the original 'view' setting */ - rv3d->view = RV3D_VIEW_USER; + /* original values */ + if (sview->camera_old) { + Object *ob_camera_old_eval = DEG_get_evaluated_object(depsgraph, sview->camera_old); + if (sview->ofs != NULL) { + sms.src.dist = ED_view3d_offset_distance(ob_camera_old_eval->obmat, sview->ofs, 0.0f); } + ED_view3d_from_object( + ob_camera_old_eval, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens); + } + /* grid draw as floor */ + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + /* use existing if exists, means multiple calls to smooth view + * won't lose the original 'view' setting */ + rv3d->view = RV3D_VIEW_USER; + } - sms.time_allowed = (double)smooth_viewtx / 1000.0; - - /* if this is view rotation only - * we can decrease the time allowed by - * the angle between quats - * this means small rotations won't lag */ - if (sview->quat && !sview->ofs && !sview->dist) { - /* scale the time allowed by the rotation */ - /* 180deg == 1.0 */ - sms.time_allowed *= (double)fabsf( - angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / - M_PI; - } + sms.time_allowed = (double)smooth_viewtx / 1000.0; - /* ensure it shows correct */ - if (sms.to_camera) { - /* use ortho if we move from an ortho view to an ortho camera */ - Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); - rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && - (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? - RV3D_ORTHO : - RV3D_PERSP); - } + /* If this is view rotation only we can decrease the time allowed by the angle between quats + * this means small rotations won't lag. */ + if (sview->quat && !sview->ofs && !sview->dist) { + /* scale the time allowed by the rotation */ + /* 180deg == 1.0 */ + sms.time_allowed *= (double)fabsf(angle_signed_normalized_qtqt(sms.dst.quat, sms.src.quat)) / + M_PI; + } - rv3d->rflag |= RV3D_NAVIGATING; + /* ensure it shows correct */ + if (sms.to_camera) { + /* use ortho if we move from an ortho view to an ortho camera */ + Object *ob_camera_eval = DEG_get_evaluated_object(depsgraph, sview->camera); + rv3d->persp = (((rv3d->is_persp == false) && (ob_camera_eval->type == OB_CAMERA) && + (((Camera *)ob_camera_eval->data)->type == CAM_ORTHO)) ? + RV3D_ORTHO : + RV3D_PERSP); + } - /* not essential but in some cases the caller will tag the area for redraw, and in that - * case we can get a flicker of the 'org' user view but we want to see 'src' */ - view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); + rv3d->rflag |= RV3D_NAVIGATING; - /* keep track of running timer! */ - if (rv3d->sms == NULL) { - rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); - } - *rv3d->sms = sms; - if (rv3d->smooth_timer) { - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - } - /* #TIMER1 is hard-coded in key-map. */ - rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); + /* not essential but in some cases the caller will tag the area for redraw, and in that + * case we can get a flicker of the 'org' user view but we want to see 'src' */ + view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); + + /* keep track of running timer! */ + if (rv3d->sms == NULL) { + rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d"); } - else { - if (sms.to_camera == false) { - copy_v3_v3(rv3d->ofs, sms.dst.ofs); - copy_qt_qt(rv3d->viewquat, sms.dst.quat); - rv3d->dist = sms.dst.dist; - v3d->lens = sms.dst.lens; + *rv3d->sms = sms; + if (rv3d->smooth_timer) { + WM_event_remove_timer(wm, win, rv3d->smooth_timer); + } + /* #TIMER1 is hard-coded in key-map. */ + rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); + } + else { + /* Animation is disabled, apply immediately. */ + if (sms.to_camera == false) { + copy_v3_v3(rv3d->ofs, sms.dst.ofs); + copy_qt_qt(rv3d->viewquat, sms.dst.quat); + rv3d->dist = sms.dst.dist; + v3d->lens = sms.dst.lens; - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - } + ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + } - if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { - view3d_boxview_copy(area, region); - } + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_copy(area, region); + } - ED_region_tag_redraw(region); + ED_region_tag_redraw(region); - WM_event_add_mousemove(win); - } + WM_event_add_mousemove(win); } } -- cgit v1.2.3 From e0394761b954759f8723bdc8f4a1686bf70954ed Mon Sep 17 00:00:00 2001 From: Jon Denning Date: Mon, 13 Sep 2021 16:12:12 +1000 Subject: GPUShader: Expose name for debugging & identifying shaders Added optional `name` argument to `GPUShader` constructor (defaults to `pyGPUShader`), and added `name` getter to `GPUShader`. Ref D12393 Reviewed By: campbellbarton, jbakker --- source/blender/gpu/GPU_shader.h | 5 ++++- source/blender/gpu/intern/gpu_shader.cc | 19 +++++++++++++++++-- source/blender/python/gpu/gpu_py_shader.c | 29 +++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index f834ee5b234..62b748b7edf 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -54,7 +54,8 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, const char *libcode, - const char *defines); + const char *defines, + const char *name); GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, @@ -85,6 +86,8 @@ void GPU_shader_free(GPUShader *shader); void GPU_shader_bind(GPUShader *shader); void GPU_shader_unbind(void); +const char *GPU_shader_get_name(GPUShader *shader); + /* Returns true if transform feedback was successfully enabled. */ bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf); void GPU_shader_transform_feedback_disable(GPUShader *shader); diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index c754a649924..9340d311472 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -229,7 +229,8 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, const char *libcode, - const char *defines) + const char *defines, + const char *name) { char *libcodecat = nullptr; @@ -240,6 +241,9 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); } + /* Use pyGPUShader as default name for shader. */ + const char *shname = name != nullptr ? name : "pyGPUShader"; + GPUShader *sh = GPU_shader_create_ex(vertcode, fragcode, geomcode, @@ -249,7 +253,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPU_SHADER_TFB_NONE, nullptr, 0, - "pyGPUShader"); + shname); MEM_SAFE_FREE(libcodecat); return sh; @@ -368,6 +372,17 @@ void GPU_shader_unbind(void) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Shader name + * \{ */ + +const char *GPU_shader_get_name(GPUShader *shader) +{ + return unwrap(shader)->name_get(); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Transform feedback * diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index 95e505b1343..1bdf9766c1b 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -105,12 +105,13 @@ static PyObject *pygpu_shader__tp_new(PyTypeObject *UNUSED(type), PyObject *args const char *geocode; const char *libcode; const char *defines; + const char *name; } params = {0}; static const char *_keywords[] = { - "vertexcode", "fragcode", "geocode", "libcode", "defines", NULL}; + "vertexcode", "fragcode", "geocode", "libcode", "defines", "name", NULL}; - static _PyArg_Parser _parser = {"ss|$sss:GPUShader.__new__", _keywords, 0}; + static _PyArg_Parser _parser = {"ss|$ssss:GPUShader.__new__", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, @@ -118,12 +119,17 @@ static PyObject *pygpu_shader__tp_new(PyTypeObject *UNUSED(type), PyObject *args ¶ms.fragcode, ¶ms.geocode, ¶ms.libcode, - ¶ms.defines)) { + ¶ms.defines, + ¶ms.name)) { return NULL; } - GPUShader *shader = GPU_shader_create_from_python( - params.vertexcode, params.fragcode, params.geocode, params.libcode, params.defines); + GPUShader *shader = GPU_shader_create_from_python(params.vertexcode, + params.fragcode, + params.geocode, + params.libcode, + params.defines, + params.name); if (shader == NULL) { PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details"); @@ -639,6 +645,13 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = { {NULL, NULL, 0, NULL}, }; +PyDoc_STRVAR(pygpu_shader_name_doc, + "The name of the shader object for debugging purposes (read-only).\n\n:type: str"); +static PyObject *pygpu_shader_name(BPyGPUShader *self) +{ + return PyUnicode_FromString(GPU_shader_get_name(self->shader)); +} + PyDoc_STRVAR( pygpu_shader_program_doc, "The name of the program object for use by the OpenGL API (read-only).\n\n:type: int"); @@ -649,6 +662,7 @@ static PyObject *pygpu_shader_program_get(BPyGPUShader *self, void *UNUSED(closu static PyGetSetDef pygpu_shader__tp_getseters[] = { {"program", (getter)pygpu_shader_program_get, (setter)NULL, pygpu_shader_program_doc, NULL}, + {"name", (getter)pygpu_shader_name, (setter)NULL, pygpu_shader_name_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -662,7 +676,8 @@ static void pygpu_shader__tp_dealloc(BPyGPUShader *self) PyDoc_STRVAR( pygpu_shader__tp_doc, - ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None)\n" + ".. class:: GPUShader(vertexcode, fragcode, geocode=None, libcode=None, defines=None, " + "name='pyGPUShader')\n" "\n" " GPUShader combines multiple GLSL shaders into a program used for drawing.\n" " It must contain at least a vertex and fragment shaders.\n" @@ -688,6 +703,8 @@ PyDoc_STRVAR( " :param libcode: Code with functions and presets to be shared between shaders.\n" " :type value: str\n" " :param defines: Preprocessor directives.\n" + " :type value: str\n" + " :param name: Name of shader code, for debugging purposes.\n" " :type value: str\n"); PyTypeObject BPyGPUShader_Type = { PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader", -- cgit v1.2.3 From 9d336576b546ebf3ad1628e9e1f6f732207ee76d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 13 Sep 2021 17:50:02 +1000 Subject: Cleanup: clang-format --- intern/cycles/render/light.cpp | 2 +- source/blender/io/usd/intern/usd_reader_curve.cc | 2 +- source/blender/makesrna/intern/rna_space.c | 3 ++- source/blender/makesrna/intern/rna_userdef.c | 3 +-- .../nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc | 7 +++++-- .../blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc | 7 +++++-- source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc | 3 ++- .../nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc | 7 +++++-- source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_material_assign.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_point_instance.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_point_separate.cc | 3 ++- source/blender/nodes/geometry/nodes/node_geo_point_translate.cc | 3 ++- 14 files changed, 34 insertions(+), 18 deletions(-) diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 5f9764cd1cc..15aa4e047b5 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -413,7 +413,7 @@ void LightManager::device_update_distribution(Device *, /* point lights */ bool use_lamp_mis = false; int light_index = 0; - + if (num_lights > 0) { float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f; foreach (Light *light, scene->lights) { diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index 7410757a9d9..12de1d82c72 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -46,7 +46,7 @@ void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTim { curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); - curve_->flag |= CU_3D; + curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 61d92d350ed..e3985b26eb0 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4941,7 +4941,8 @@ static void rna_def_space_view3d(BlenderRNA *brna) prop = RNA_def_property(srna, "lock_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "viewlock", RV3D_LOCK_ROTATION); - RNA_def_property_ui_text(prop, "Lock Rotation", "Lock view rotation of side views to Top/Front/Right"); + RNA_def_property_ui_text( + prop, "Lock Rotation", "Lock view rotation of side views to Top/Front/Right"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_RegionView3D_quadview_update"); prop = RNA_def_property(srna, "show_sync_view", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index b3a3d0198fe..13aea804957 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6080,8 +6080,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) prop = RNA_def_property(srna, "use_filter_files", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_FILTERFILEEXTS); - RNA_def_property_ui_text( - prop, "Filter Files", "Enable filtering of files in the File Browser"); + RNA_def_property_ui_text(prop, "Filter Files", "Enable filtering of files in the File Browser"); prop = RNA_def_property(srna, "show_recent_locations", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_HIDE_RECENT); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc index 232e0376a11..de0090406c6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc @@ -157,8 +157,11 @@ void register_node_type_geo_attribute_separate_xyz() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, "Attribute Separate XYZ", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, + "Attribute Separate XYZ", + NODE_CLASS_ATTRIBUTE, + 0); ntype.declare = blender::nodes::geo_node_attribute_separate_xyz_declare; node_type_init(&ntype, blender::nodes::geo_node_attribute_separate_xyz_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_separate_xyz_update); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index 72714cff6b7..59903050f88 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -555,8 +555,11 @@ void register_node_type_geo_attribute_vector_math() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, + "Attribute Vector Math", + NODE_CLASS_ATTRIBUTE, + 0); ntype.declare = blender::nodes::geo_node_attribute_vector_math_declare; ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec; ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_math_layout; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 3312733d2fe..2cb75eda202 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -121,7 +121,8 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc index c5373d8f9ac..dfcae2e65b0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc @@ -127,8 +127,11 @@ void register_node_type_geo_select_by_handle_type() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, + GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, + "Select by Handle Type", + NODE_CLASS_GEOMETRY, + 0); ntype.declare = blender::nodes::geo_node_select_by_handle_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_select_by_handle_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 9e116770df5..0522f2b8981 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -377,7 +377,8 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; node_type_storage(&ntype, diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 0142a81ba92..5845eb357e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -668,7 +668,8 @@ void register_node_type_geo_delete_geometry() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_delete_geometry_declare; ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 60a9a8ced33..d7d3d0eded8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -87,7 +87,8 @@ void register_node_type_geo_material_assign() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_assign_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 987aee7c270..902ccfff179 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -259,7 +259,8 @@ void register_node_type_geo_point_instance() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_INSTANCE, "Point Instance", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_instance_init); node_type_storage( &ntype, "NodeGeometryPointInstance", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc index 7849b59b6b1..48b6676c1dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -164,7 +164,8 @@ void register_node_type_geo_point_separate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_point_instance_declare; ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; ntype.geometry_node_execute_supports_laziness = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc index ec78223ad0b..f2fce45c57b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc @@ -95,7 +95,8 @@ void register_node_type_geo_point_translate() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINT_TRANSLATE, "Point Translate", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_point_translate_init); node_type_update(&ntype, blender::nodes::geo_node_point_translate_update); node_type_storage(&ntype, -- cgit v1.2.3 From 9b2b32a3338d873529a9b2c402feae4e9d25afdf Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 13 Sep 2021 17:50:41 +1000 Subject: Fix T84638: Wrong scale for primitives with radius Creating some primitives allows for a scale value (via python) that will scale the object accordingly. For objects with a radius parameter (like cylinders, spheres, etc.) passing a scale different to (1,1,1) would result in unexpected behavior. For example: `>>> bpy.ops.mesh.primitive_uv_sphere_add(radius=2, scale=(1,1,2))` We would expect this to create a sphere with a radius of 2 (dimensions 4,4,4) and then be scaled *2 along the z-axis (dimensions 4,4,8). But this would previously create a scaled sphere with dimensions (2,2,4). The scale was simply divided by two. Maybe because the "radius" parameter for creating the primitives was confusingly named "diameter" (but used as the radius). The fix adds a scale parameter to `ED_object_new_primitive_matrix` and also renames the wrongly named "diameter" parameters to "radius". Reviewed By: campbellbarton Maniphest Tasks: T84638 Ref D10093 --- source/blender/bmesh/intern/bmesh_opdefines.c | 8 ++--- source/blender/bmesh/operators/bmo_primitive.c | 43 +++++++++++++------------- source/blender/editors/curve/editcurve_add.c | 8 ++--- source/blender/editors/include/ED_object.h | 1 + source/blender/editors/mesh/editmesh_add.c | 14 +++------ source/blender/editors/object/object_add.c | 39 +++++++++++++---------- 6 files changed, 57 insertions(+), 56 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index b63a09a97a6..7865c79323d 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1587,7 +1587,7 @@ static BMOpDefine bmo_create_uvsphere_def = { /* slots_in */ {{"u_segments", BMO_OP_SLOT_INT}, /* number of u segments */ {"v_segments", BMO_OP_SLOT_INT}, /* number of v segment */ - {"diameter", BMO_OP_SLOT_FLT}, /* diameter */ + {"radius", BMO_OP_SLOT_FLT}, /* radius */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ {{'\0'}}, @@ -1610,7 +1610,7 @@ static BMOpDefine bmo_create_icosphere_def = { "create_icosphere", /* slots_in */ {{"subdivisions", BMO_OP_SLOT_INT}, /* how many times to recursively subdivide the sphere */ - {"diameter", BMO_OP_SLOT_FLT}, /* diameter */ + {"radius", BMO_OP_SLOT_FLT}, /* radius */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ {{'\0'}}, @@ -1656,8 +1656,8 @@ static BMOpDefine bmo_create_cone_def = { {{"cap_ends", BMO_OP_SLOT_BOOL}, /* whether or not to fill in the ends with faces */ {"cap_tris", BMO_OP_SLOT_BOOL}, /* fill ends with triangles instead of ngons */ {"segments", BMO_OP_SLOT_INT}, /* number of vertices in the base circle */ - {"diameter1", BMO_OP_SLOT_FLT}, /* diameter of one end */ - {"diameter2", BMO_OP_SLOT_FLT}, /* diameter of the opposite */ + {"radius1", BMO_OP_SLOT_FLT}, /* radius of one end */ + {"radius2", BMO_OP_SLOT_FLT}, /* radius of the opposite */ {"depth", BMO_OP_SLOT_FLT}, /* distance between ends */ {"matrix", BMO_OP_SLOT_MAT}, /* matrix to multiply the new geometry with */ {"calc_uvs", BMO_OP_SLOT_BOOL}, /* calculate default UVs */ diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 8d5963cfb1c..d8047499780 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -854,7 +854,7 @@ void BM_mesh_calc_uvs_grid(BMesh *bm, void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) { - const float dia = BMO_slot_float_get(op->slots_in, "diameter"); + const float rad = BMO_slot_float_get(op->slots_in, "radius"); const int seg = BMO_slot_int_get(op->slots_in, "u_segments"); const int tot = BMO_slot_int_get(op->slots_in, "v_segments"); @@ -881,8 +881,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) const float phi = M_PI * ((double)a / (double)tot); vec[0] = 0.0; - vec[1] = dia * sinf(phi); - vec[2] = dia * cosf(phi); + vec[1] = rad * sinf(phi); + vec[2] = rad * cosf(phi); eve = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); BMO_vert_flag_enable(bm, eve, VERT_MARK); @@ -921,12 +921,12 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) { float len, len2, vec2[3]; - len = 2 * dia * sinf(phid / 2.0f); + len = 2 * rad * sinf(phid / 2.0f); /* Length of one segment in shortest parallel. */ - vec[0] = dia * sinf(phid); + vec[0] = rad * sinf(phid); vec[1] = 0.0f; - vec[2] = dia * cosf(phid); + vec[2] = rad * cosf(phid); mul_v3_m3v3(vec2, cmat, vec); len2 = len_v3v3(vec, vec2); @@ -973,8 +973,8 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op) void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) { - const float dia = BMO_slot_float_get(op->slots_in, "diameter"); - const float dia_div = dia / 200.0f; + const float rad = BMO_slot_float_get(op->slots_in, "radius"); + const float rad_div = rad / 200.0f; const int subdiv = BMO_slot_int_get(op->slots_in, "subdivisions"); const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); @@ -994,9 +994,9 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) /* phi = 0.25f * (float)M_PI; */ /* UNUSED */ for (a = 0; a < 12; a++) { - vec[0] = dia_div * icovert[a][0]; - vec[1] = dia_div * icovert[a][1]; - vec[2] = dia_div * icovert[a][2]; + vec[0] = rad_div * icovert[a][0]; + vec[1] = rad_div * icovert[a][1]; + vec[2] = rad_div * icovert[a][2]; eva[a] = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); BMO_vert_flag_enable(bm, eva[a], VERT_MARK); @@ -1041,7 +1041,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op) "cuts=%i " "use_grid_fill=%b use_sphere=%b", EDGE_MARK, - dia, + rad, (1 << (subdiv - 1)) - 1, true, true); @@ -1392,8 +1392,8 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2; BMFace *f; float vec[3], mat[4][4]; - const float dia1 = BMO_slot_float_get(op->slots_in, "diameter1"); - const float dia2 = BMO_slot_float_get(op->slots_in, "diameter2"); + const float rad1 = BMO_slot_float_get(op->slots_in, "radius1"); + const float rad2 = BMO_slot_float_get(op->slots_in, "radius2"); const float depth_half = 0.5f * BMO_slot_float_get(op->slots_in, "depth"); int segs = BMO_slot_int_get(op->slots_in, "segments"); const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends"); @@ -1431,15 +1431,14 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < segs; i++) { /* Calculate with doubles for higher precision, see: T87779. */ const float phi = (2.0 * M_PI) * ((double)i / (double)segs); - - vec[0] = dia1 * sinf(phi); - vec[1] = dia1 * cosf(phi); + vec[0] = rad1 * sinf(phi); + vec[1] = rad1 * cosf(phi); vec[2] = -depth_half; mul_m4_v3(mat, vec); v1 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); - vec[0] = dia2 * sinf(phi); - vec[1] = dia2 * cosf(phi); + vec[0] = rad2 * sinf(phi); + vec[1] = rad2 * cosf(phi); vec[2] = depth_half; mul_m4_v3(mat, vec); v2 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP); @@ -1497,11 +1496,11 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) } if (calc_uvs) { - BM_mesh_calc_uvs_cone(bm, mat, dia2, dia1, segs, cap_ends, FACE_MARK, cd_loop_uv_offset); + BM_mesh_calc_uvs_cone(bm, mat, rad2, rad1, segs, cap_ends, FACE_MARK, cd_loop_uv_offset); } /* Collapse vertices at the first end. */ - if (dia1 == 0.0f) { + if (rad1 == 0.0f) { if (cap_ends) { BM_vert_kill(bm, cent1); } @@ -1513,7 +1512,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) } /* Collapse vertices at the second end. */ - if (dia2 == 0.0f) { + if (rad2 == 0.0f) { if (cap_ends) { BM_vert_kill(bm, cent2); } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 19aad20cbc1..75fb17e8cc1 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -515,7 +515,6 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) bool newob = false; bool enter_editmode; ushort local_view_bits; - float dia; float loc[3], rot[3]; float mat[4][4]; @@ -555,9 +554,10 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } } - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); - dia = RNA_float_get(op->ptr, "radius"); - mul_mat3_m4_fl(mat, dia); + float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); + ED_object_new_primitive_matrix(C, obedit, loc, rot, scale, mat); nu = ED_curve_add_nurbs_primitive(C, obedit, mat, type, newob); editnurb = object_editcurve_get(obedit); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index a9cf04e1ad7..5397cd95ace 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -286,6 +286,7 @@ float ED_object_new_primitive_matrix(struct bContext *C, struct Object *obedit, const float loc[3], const float rot[3], + const float scale[3], float primmat[4][4]); /* Avoid allowing too much insane values even by typing diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index a64b90e15a3..c826da74010 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -73,11 +73,7 @@ static Object *make_prim_init(bContext *C, r_creation_data->was_editmode = true; } - ED_object_new_primitive_matrix(C, obedit, loc, rot, r_creation_data->mat); - - if (scale) { - rescale_m4(r_creation_data->mat, scale); - } + ED_object_new_primitive_matrix(C, obedit, loc, rot, scale, r_creation_data->mat); return obedit; } @@ -351,7 +347,7 @@ static int add_primitive_cylinder_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "create_cone segments=%i radius1=%f radius2=%f cap_ends=%b " "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius"), @@ -427,7 +423,7 @@ static int add_primitive_cone_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b " + "create_cone segments=%i radius1=%f radius2=%f cap_ends=%b " "cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "vertices"), RNA_float_get(op->ptr, "radius1"), @@ -642,7 +638,7 @@ static int add_primitive_uvsphere_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_uvsphere u_segments=%i v_segments=%i radius=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "segments"), RNA_int_get(op->ptr, "ring_count"), RNA_float_get(op->ptr, "radius"), @@ -710,7 +706,7 @@ static int add_primitive_icosphere_exec(bContext *C, wmOperator *op) op, "verts.out", false, - "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b", RNA_int_get(op->ptr, "subdivisions"), RNA_float_get(op->ptr, "radius"), creation_data.mat, diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index f95e3f3b236..beadbf2689e 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -332,8 +332,12 @@ void ED_object_base_init_transform_on_add(Object *object, const float loc[3], co /* Uses context to figure out transform for primitive. * Returns standard diameter. */ -float ED_object_new_primitive_matrix( - bContext *C, Object *obedit, const float loc[3], const float rot[3], float r_primmat[4][4]) +float ED_object_new_primitive_matrix(bContext *C, + Object *obedit, + const float loc[3], + const float rot[3], + const float scale[3], + float r_primmat[4][4]) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); @@ -356,6 +360,10 @@ float ED_object_new_primitive_matrix( invert_m3_m3(imat, mat); mul_m3_v3(imat, r_primmat[3]); + if (scale != NULL) { + rescale_m4(r_primmat, scale); + } + { const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); @@ -863,7 +871,7 @@ static int effector_add_exec(bContext *C, wmOperator *op) ED_object_editmode_enter_ex(bmain, scene, ob, 0); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); mul_mat3_m4_fl(mat, dia); BLI_addtail(&cu->editnurb->nurbs, ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); @@ -999,7 +1007,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) } float mat[4][4]; - ED_object_new_primitive_matrix(C, obedit, loc, rot, mat); + ED_object_new_primitive_matrix(C, obedit, loc, rot, NULL, mat); /* Halving here is done to account for constant values from #BKE_mball_element_add. * While the default radius of the resulting meta element is 2, * we want to pass in 1 so other values such as resolution are scaled by 1.0. */ @@ -1365,30 +1373,28 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_EMPTY: { float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); ED_gpencil_create_blank(C, ob, mat); break; } case GP_STROKE: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_stroke(C, ob, mat); break; } case GP_MONKEY: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_monkey(C, ob, mat); break; @@ -1397,12 +1403,11 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_LRT_COLLECTION: case GP_LRT_OBJECT: { float radius = RNA_float_get(op->ptr, "radius"); + float scale[3]; + copy_v3_fl(scale, radius); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, mat); - mul_v3_fl(mat[0], radius); - mul_v3_fl(mat[1], radius); - mul_v3_fl(mat[2], radius); + ED_object_new_primitive_matrix(C, ob, loc, rot, scale, mat); ED_gpencil_create_lineart(C, ob); -- cgit v1.2.3 From eaa35b27e0af9547ed947d672bea660df3d688a0 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 13 Sep 2021 10:42:54 +0200 Subject: Fix T91311: incorrect batch generation for instances This was a mistake in {rB5a9a16334c573c4566dc9b2a314cf0d0ccdcb54f}. --- source/blender/draw/intern/draw_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index aca645acc09..47adc0acc60 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -695,7 +695,7 @@ static void duplidata_value_free(void *val) static void duplidata_key_free(void *key) { DupliKey *dupli_key = (DupliKey *)key; - if (dupli_key->ob_data == NULL) { + if (dupli_key->ob_data == dupli_key->ob->data) { drw_batch_cache_generate_requested(dupli_key->ob); } else { -- cgit v1.2.3 From 25550c210a7278d5e17d592b3eb0bba652c893c5 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 13 Sep 2021 11:30:32 +0200 Subject: Fix regression test after recent fix Was cased by 9b2b32a3338. --- .../blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index d77551367a9..5ea7165ac31 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -44,7 +44,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) BMO_op_callf(bm, BMO_FLAG_DEFAULTS, - "create_icosphere subdivisions=%i diameter=%f matrix=%m4 calc_uvs=%b", + "create_icosphere subdivisions=%i radius=%f matrix=%m4 calc_uvs=%b", subdivisions, std::abs(radius), transform.values, -- cgit v1.2.3 From 9d6e960e2c3b65f7b4fbe35db3b832f1fd3d7f30 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 13 Sep 2021 11:38:54 +0200 Subject: Fix `make_utils.py` recent API breackage. rB546314fc9669 broke `svn_libraries_base_url` utils API compatibility for no good reasons, making new `branch` argument explicitely optional. Reported on chat by Michael Kowalski (@makowalski), thanks! --- build_files/utils/make_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py index 7cd23baad68..db352ff7e16 100755 --- a/build_files/utils/make_utils.py +++ b/build_files/utils/make_utils.py @@ -70,7 +70,7 @@ def git_branch_release_version(branch, tag): return release_version -def svn_libraries_base_url(release_version, branch): +def svn_libraries_base_url(release_version, branch=None): if release_version: svn_branch = "tags/blender-" + release_version + "-release" elif branch: -- cgit v1.2.3 From f6ebbfe7dee663c4d5587e386882df42a24ffa93 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 13 Sep 2021 11:54:05 +0200 Subject: Nodes: move some texture nodes to c++ This is necessary to be able to use them in geometry nodes. --- source/blender/nodes/CMakeLists.txt | 10 +- .../nodes/shader/nodes/node_shader_tex_gradient.c | 81 --------- .../nodes/shader/nodes/node_shader_tex_gradient.cc | 82 +++++++++ .../nodes/shader/nodes/node_shader_tex_musgrave.c | 159 ----------------- .../nodes/shader/nodes/node_shader_tex_musgrave.cc | 151 ++++++++++++++++ .../nodes/shader/nodes/node_shader_tex_noise.c | 103 ----------- .../nodes/shader/nodes/node_shader_tex_noise.cc | 107 ++++++++++++ .../nodes/shader/nodes/node_shader_tex_voronoi.c | 194 --------------------- .../nodes/shader/nodes/node_shader_tex_voronoi.cc | 194 +++++++++++++++++++++ .../shader/nodes/node_shader_tex_white_noise.c | 81 --------- .../shader/nodes/node_shader_tex_white_noise.cc | 79 +++++++++ 11 files changed, 618 insertions(+), 623 deletions(-) delete mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_gradient.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc delete mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc delete mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_noise.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_noise.cc delete mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc delete mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c create mode 100644 source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index ac2200d496b..614be1d7229 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -292,16 +292,16 @@ set(SRC shader/nodes/node_shader_tex_checker.c shader/nodes/node_shader_tex_coord.c shader/nodes/node_shader_tex_environment.c - shader/nodes/node_shader_tex_gradient.c + shader/nodes/node_shader_tex_gradient.cc shader/nodes/node_shader_tex_image.c shader/nodes/node_shader_tex_magic.c - shader/nodes/node_shader_tex_musgrave.c - shader/nodes/node_shader_tex_noise.c + shader/nodes/node_shader_tex_musgrave.cc + shader/nodes/node_shader_tex_noise.cc shader/nodes/node_shader_tex_pointdensity.c shader/nodes/node_shader_tex_sky.c - shader/nodes/node_shader_tex_voronoi.c + shader/nodes/node_shader_tex_voronoi.cc shader/nodes/node_shader_tex_wave.c - shader/nodes/node_shader_tex_white_noise.c + shader/nodes/node_shader_tex_white_noise.cc shader/nodes/node_shader_uvAlongStroke.c shader/nodes/node_shader_uvmap.c shader/nodes/node_shader_valToRgb.cc diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c deleted file mode 100644 index e3d4bad2bf8..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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) 2005 Gradienter Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** BLEND ******************** */ - -static bNodeSocketTemplate sh_node_tex_gradient_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_gradient_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexGradient *tex = MEM_callocN(sizeof(NodeTexGradient), "NodeTexGradient"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->gradient_type = SHD_BLEND_LINEAR; - - node->storage = tex; -} - -static int node_shader_gpu_tex_gradient(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - NodeTexGradient *tex = (NodeTexGradient *)node->storage; - float gradient_type = tex->gradient_type; - return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type)); -} - -/* node type definition */ -void register_node_type_sh_tex_gradient(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_gradient_in, sh_node_tex_gradient_out); - node_type_init(&ntype, node_shader_init_tex_gradient); - node_type_storage( - &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_gradient); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc new file mode 100644 index 00000000000..0c0d75179a9 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -0,0 +1,82 @@ +/* + * 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) 2005 Gradienter Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +/* **************** BLEND ******************** */ + +static bNodeSocketTemplate sh_node_tex_gradient_in[] = { + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_tex_gradient_out[] = { + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, + {SOCK_FLOAT, + N_("Fac"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_FACTOR, + SOCK_NO_INTERNAL_LINK}, + {-1, ""}, +}; + +static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexGradient *tex = (NodeTexGradient *)MEM_callocN(sizeof(NodeTexGradient), + "NodeTexGradient"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->gradient_type = SHD_BLEND_LINEAR; + + node->storage = tex; +} + +static int node_shader_gpu_tex_gradient(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexGradient *tex = (NodeTexGradient *)node->storage; + float gradient_type = tex->gradient_type; + return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type)); +} + +/* node type definition */ +void register_node_type_sh_tex_gradient(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); + node_type_socket_templates(&ntype, sh_node_tex_gradient_in, sh_node_tex_gradient_out); + node_type_init(&ntype, node_shader_init_tex_gradient); + node_type_storage( + &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_gradient); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c deleted file mode 100644 index 420c5b75926..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** MUSGRAVE ******************** */ - -static bNodeSocketTemplate sh_node_tex_musgrave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Dimension"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Lacunarity"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Gain"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_musgrave_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexMusgrave *tex = MEM_callocN(sizeof(NodeTexMusgrave), "NodeTexMusgrave"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->musgrave_type = SHD_MUSGRAVE_FBM; - tex->dimensions = 3; - - node->storage = tex; -} - -static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; - int dimensions = tex->dimensions; - int type = tex->musgrave_type; - - static const char *names[][5] = { - [SHD_MUSGRAVE_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_multi_fractal_1d", - "node_tex_musgrave_multi_fractal_2d", - "node_tex_musgrave_multi_fractal_3d", - "node_tex_musgrave_multi_fractal_4d", - }, - [SHD_MUSGRAVE_FBM] = - { - "", - "node_tex_musgrave_fBm_1d", - "node_tex_musgrave_fBm_2d", - "node_tex_musgrave_fBm_3d", - "node_tex_musgrave_fBm_4d", - }, - [SHD_MUSGRAVE_HYBRID_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_hybrid_multi_fractal_1d", - "node_tex_musgrave_hybrid_multi_fractal_2d", - "node_tex_musgrave_hybrid_multi_fractal_3d", - "node_tex_musgrave_hybrid_multi_fractal_4d", - }, - [SHD_MUSGRAVE_RIDGED_MULTIFRACTAL] = - { - "", - "node_tex_musgrave_ridged_multi_fractal_1d", - "node_tex_musgrave_ridged_multi_fractal_2d", - "node_tex_musgrave_ridged_multi_fractal_3d", - "node_tex_musgrave_ridged_multi_fractal_4d", - }, - [SHD_MUSGRAVE_HETERO_TERRAIN] = - { - "", - "node_tex_musgrave_hetero_terrain_1d", - "node_tex_musgrave_hetero_terrain_2d", - "node_tex_musgrave_hetero_terrain_3d", - "node_tex_musgrave_hetero_terrain_4d", - }, - }; - - BLI_assert(type >= 0 && type < 5); - BLI_assert(dimensions > 0 && dimensions < 5); - - return GPU_stack_link(mat, node, names[type][dimensions], in, out); -} - -static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; - - bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); - bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); - bNodeSocket *inOffsetSock = nodeFindSocket(node, SOCK_IN, "Offset"); - bNodeSocket *inGainSock = nodeFindSocket(node, SOCK_IN, "Gain"); - - nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); - nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); - nodeSetSocketAvailability(inOffsetSock, - tex->musgrave_type != SHD_MUSGRAVE_MULTIFRACTAL && - tex->musgrave_type != SHD_MUSGRAVE_FBM); - nodeSetSocketAvailability(inGainSock, - tex->musgrave_type == SHD_MUSGRAVE_HYBRID_MULTIFRACTAL || - tex->musgrave_type == SHD_MUSGRAVE_RIDGED_MULTIFRACTAL); - - bNodeSocket *outFacSock = nodeFindSocket(node, SOCK_OUT, "Fac"); - node_sock_label(outFacSock, "Height"); -} - -void register_node_type_sh_tex_musgrave(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_musgrave_in, sh_node_tex_musgrave_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_musgrave); - node_type_storage( - &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_musgrave); - node_type_update(&ntype, node_shader_update_tex_musgrave); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc new file mode 100644 index 00000000000..f5e9aef3aad --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -0,0 +1,151 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +/* **************** MUSGRAVE ******************** */ + +static bNodeSocketTemplate sh_node_tex_musgrave_in[] = { + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, + {SOCK_FLOAT, N_("Dimension"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + {SOCK_FLOAT, N_("Lacunarity"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + {SOCK_FLOAT, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Gain"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_tex_musgrave_out[] = { + {SOCK_FLOAT, + N_("Fac"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_FACTOR, + SOCK_NO_INTERNAL_LINK}, + {-1, ""}, +}; + +static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexMusgrave *tex = (NodeTexMusgrave *)MEM_callocN(sizeof(NodeTexMusgrave), + "NodeTexMusgrave"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->musgrave_type = SHD_MUSGRAVE_FBM; + tex->dimensions = 3; + + node->storage = tex; +} + +static const char *gpu_shader_name_get(const int type, const int dimensions) +{ + BLI_assert(type >= 0 && type < 5); + BLI_assert(dimensions > 0 && dimensions < 5); + + switch (type) { + case SHD_MUSGRAVE_MULTIFRACTAL: + return std::array{"node_tex_musgrave_multi_fractal_1d", + "node_tex_musgrave_multi_fractal_2d", + "node_tex_musgrave_multi_fractal_3d", + "node_tex_musgrave_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_FBM: + return std::array{"node_tex_musgrave_fBm_1d", + "node_tex_musgrave_fBm_2d", + "node_tex_musgrave_fBm_3d", + "node_tex_musgrave_fBm_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_HYBRID_MULTIFRACTAL: + return std::array{"node_tex_musgrave_hybrid_multi_fractal_1d", + "node_tex_musgrave_hybrid_multi_fractal_2d", + "node_tex_musgrave_hybrid_multi_fractal_3d", + "node_tex_musgrave_hybrid_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_RIDGED_MULTIFRACTAL: + return std::array{"node_tex_musgrave_ridged_multi_fractal_1d", + "node_tex_musgrave_ridged_multi_fractal_2d", + "node_tex_musgrave_ridged_multi_fractal_3d", + "node_tex_musgrave_ridged_multi_fractal_4d"}[dimensions - 1]; + case SHD_MUSGRAVE_HETERO_TERRAIN: + return std::array{"node_tex_musgrave_hetero_terrain_1d", + "node_tex_musgrave_hetero_terrain_2d", + "node_tex_musgrave_hetero_terrain_3d", + "node_tex_musgrave_hetero_terrain_4d"}[dimensions - 1]; + } + return nullptr; +} + +static int node_shader_gpu_tex_musgrave(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; + int dimensions = tex->dimensions; + int type = tex->musgrave_type; + + const char *name = gpu_shader_name_get(type, dimensions); + + return GPU_stack_link(mat, node, name, in, out); +} + +static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage; + + bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); + bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); + bNodeSocket *inOffsetSock = nodeFindSocket(node, SOCK_IN, "Offset"); + bNodeSocket *inGainSock = nodeFindSocket(node, SOCK_IN, "Gain"); + + nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); + nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); + nodeSetSocketAvailability(inOffsetSock, + tex->musgrave_type != SHD_MUSGRAVE_MULTIFRACTAL && + tex->musgrave_type != SHD_MUSGRAVE_FBM); + nodeSetSocketAvailability(inGainSock, + tex->musgrave_type == SHD_MUSGRAVE_HYBRID_MULTIFRACTAL || + tex->musgrave_type == SHD_MUSGRAVE_RIDGED_MULTIFRACTAL); + + bNodeSocket *outFacSock = nodeFindSocket(node, SOCK_OUT, "Fac"); + node_sock_label(outFacSock, "Height"); +} + +void register_node_type_sh_tex_musgrave(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); + node_type_socket_templates(&ntype, sh_node_tex_musgrave_in, sh_node_tex_musgrave_out); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, node_shader_init_tex_musgrave); + node_type_storage( + &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_musgrave); + node_type_update(&ntype, node_shader_update_tex_musgrave); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c deleted file mode 100644 index 7b67c2d1f2e..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** NOISE ******************** */ - -static bNodeSocketTemplate sh_node_tex_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_noise_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexNoise *tex = MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->dimensions = 3; - - node->storage = tex; -} - -static int node_shader_gpu_tex_noise(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - NodeTexNoise *tex = (NodeTexNoise *)node->storage; - static const char *names[] = { - "", - "node_noise_texture_1d", - "node_noise_texture_2d", - "node_noise_texture_3d", - "node_noise_texture_4d", - }; - return GPU_stack_link(mat, node, names[tex->dimensions], in, out); -} - -static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); - bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); - - NodeTexNoise *tex = (NodeTexNoise *)node->storage; - nodeSetSocketAvailability(sockVector, tex->dimensions != 1); - nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4); -} - -/* node type definition */ -void register_node_type_sh_tex_noise(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out); - node_type_init(&ntype, node_shader_init_tex_noise); - node_type_storage( - &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_noise); - node_type_update(&ntype, node_shader_update_tex_noise); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc new file mode 100644 index 00000000000..de8e0916f4d --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -0,0 +1,107 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +/* **************** NOISE ******************** */ + +static bNodeSocketTemplate sh_node_tex_noise_in[] = { + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, + {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_tex_noise_out[] = { + {SOCK_FLOAT, + N_("Fac"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_FACTOR, + SOCK_NO_INTERNAL_LINK}, + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, + {-1, ""}, +}; + +static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexNoise *tex = (NodeTexNoise *)MEM_callocN(sizeof(NodeTexNoise), "NodeTexNoise"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->dimensions = 3; + + node->storage = tex; +} + +static const char *gpu_shader_get_name(const int dimensions) +{ + BLI_assert(dimensions >= 1 && dimensions <= 4); + return std::array{"node_noise_texture_1d", + "node_noise_texture_2d", + "node_noise_texture_3d", + "node_noise_texture_4d"}[dimensions - 1]; + return nullptr; +} + +static int node_shader_gpu_tex_noise(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexNoise *tex = (NodeTexNoise *)node->storage; + const char *name = gpu_shader_get_name(tex->dimensions); + return GPU_stack_link(mat, node, name, in, out); +} + +static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); + bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); + + NodeTexNoise *tex = (NodeTexNoise *)node->storage; + nodeSetSocketAvailability(sockVector, tex->dimensions != 1); + nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4); +} + +/* node type definition */ +void register_node_type_sh_tex_noise(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); + node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out); + node_type_init(&ntype, node_shader_init_tex_noise); + node_type_storage( + &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_noise); + node_type_update(&ntype, node_shader_update_tex_noise); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c deleted file mode 100644 index 64dc44fc67d..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** VORONOI ******************** */ - -static bNodeSocketTemplate sh_node_tex_voronoi_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Smoothness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Exponent"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 32.0f}, - {SOCK_FLOAT, N_("Randomness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_voronoi_out[] = { - {SOCK_FLOAT, - N_("Distance"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_VECTOR, - N_("Position"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Radius"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexVoronoi *tex = MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->dimensions = 3; - tex->distance = SHD_VORONOI_EUCLIDEAN; - tex->feature = SHD_VORONOI_F1; - - node->storage = tex; -} - -static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - static const char *names[][5] = { - [SHD_VORONOI_F1] = - { - "", - "node_tex_voronoi_f1_1d", - "node_tex_voronoi_f1_2d", - "node_tex_voronoi_f1_3d", - "node_tex_voronoi_f1_4d", - }, - [SHD_VORONOI_F2] = - { - "", - "node_tex_voronoi_f2_1d", - "node_tex_voronoi_f2_2d", - "node_tex_voronoi_f2_3d", - "node_tex_voronoi_f2_4d", - }, - [SHD_VORONOI_SMOOTH_F1] = - { - "", - "node_tex_voronoi_smooth_f1_1d", - "node_tex_voronoi_smooth_f1_2d", - "node_tex_voronoi_smooth_f1_3d", - "node_tex_voronoi_smooth_f1_4d", - }, - [SHD_VORONOI_DISTANCE_TO_EDGE] = - { - "", - "node_tex_voronoi_distance_to_edge_1d", - "node_tex_voronoi_distance_to_edge_2d", - "node_tex_voronoi_distance_to_edge_3d", - "node_tex_voronoi_distance_to_edge_4d", - }, - [SHD_VORONOI_N_SPHERE_RADIUS] = - { - "", - "node_tex_voronoi_n_sphere_radius_1d", - "node_tex_voronoi_n_sphere_radius_2d", - "node_tex_voronoi_n_sphere_radius_3d", - "node_tex_voronoi_n_sphere_radius_4d", - }, - }; - - NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; - float metric = tex->distance; - - BLI_assert(tex->feature >= 0 && tex->feature < 5); - BLI_assert(tex->dimensions > 0 && tex->dimensions < 5); - - return GPU_stack_link( - mat, node, names[tex->feature][tex->dimensions], in, out, GPU_constant(&metric)); -} - -static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); - bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); - bNodeSocket *inSmoothnessSock = nodeFindSocket(node, SOCK_IN, "Smoothness"); - bNodeSocket *inExponentSock = nodeFindSocket(node, SOCK_IN, "Exponent"); - - bNodeSocket *outDistanceSock = nodeFindSocket(node, SOCK_OUT, "Distance"); - bNodeSocket *outColorSock = nodeFindSocket(node, SOCK_OUT, "Color"); - bNodeSocket *outPositionSock = nodeFindSocket(node, SOCK_OUT, "Position"); - bNodeSocket *outWSock = nodeFindSocket(node, SOCK_OUT, "W"); - bNodeSocket *outRadiusSock = nodeFindSocket(node, SOCK_OUT, "Radius"); - - NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; - - nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); - nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); - nodeSetSocketAvailability( - inExponentSock, - tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && - !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); - nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); - nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); - nodeSetSocketAvailability(outColorSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); - nodeSetSocketAvailability(outPositionSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - tex->dimensions != 1); - nodeSetSocketAvailability(outWSock, - tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && - tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - (tex->dimensions == 1 || tex->dimensions == 4)); - nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); -} - -void register_node_type_sh_tex_voronoi(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out); - node_type_init(&ntype, node_shader_init_tex_voronoi); - node_type_storage( - &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); - node_type_update(&ntype, node_shader_update_tex_voronoi); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc new file mode 100644 index 00000000000..1cc715d99ea --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -0,0 +1,194 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +/* **************** VORONOI ******************** */ + +static bNodeSocketTemplate sh_node_tex_voronoi_in[] = { + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Smoothness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Exponent"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 32.0f}, + {SOCK_FLOAT, N_("Randomness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {-1, ""}, +}; + +static bNodeSocketTemplate sh_node_tex_voronoi_out[] = { + {SOCK_FLOAT, + N_("Distance"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_NONE, + SOCK_NO_INTERNAL_LINK}, + {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, + {SOCK_VECTOR, + N_("Position"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_NONE, + SOCK_NO_INTERNAL_LINK}, + {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, + {SOCK_FLOAT, + N_("Radius"), + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + PROP_NONE, + SOCK_NO_INTERNAL_LINK}, + {-1, ""}, +}; + +static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexVoronoi *tex = (NodeTexVoronoi *)MEM_callocN(sizeof(NodeTexVoronoi), "NodeTexVoronoi"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->dimensions = 3; + tex->distance = SHD_VORONOI_EUCLIDEAN; + tex->feature = SHD_VORONOI_F1; + + node->storage = tex; +} + +static const char *gpu_shader_get_name(const int feature, const int dimensions) +{ + BLI_assert(feature >= 0 && feature < 5); + BLI_assert(dimensions > 0 && dimensions < 5); + + switch (feature) { + case SHD_VORONOI_F1: + return std::array{ + "node_tex_voronoi_f1_1d", + "node_tex_voronoi_f1_2d", + "node_tex_voronoi_f1_3d", + "node_tex_voronoi_f1_4d", + }[dimensions - 1]; + case SHD_VORONOI_F2: + return std::array{ + "node_tex_voronoi_f2_1d", + "node_tex_voronoi_f2_2d", + "node_tex_voronoi_f2_3d", + "node_tex_voronoi_f2_4d", + }[dimensions - 1]; + case SHD_VORONOI_SMOOTH_F1: + return std::array{ + "node_tex_voronoi_smooth_f1_1d", + "node_tex_voronoi_smooth_f1_2d", + "node_tex_voronoi_smooth_f1_3d", + "node_tex_voronoi_smooth_f1_4d", + }[dimensions - 1]; + case SHD_VORONOI_DISTANCE_TO_EDGE: + return std::array{ + "node_tex_voronoi_distance_to_edge_1d", + "node_tex_voronoi_distance_to_edge_2d", + "node_tex_voronoi_distance_to_edge_3d", + "node_tex_voronoi_distance_to_edge_4d", + }[dimensions - 1]; + case SHD_VORONOI_N_SPHERE_RADIUS: + return std::array{ + "node_tex_voronoi_n_sphere_radius_1d", + "node_tex_voronoi_n_sphere_radius_2d", + "node_tex_voronoi_n_sphere_radius_3d", + "node_tex_voronoi_n_sphere_radius_4d", + }[dimensions - 1]; + } + return nullptr; +} + +static int node_shader_gpu_tex_voronoi(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; + float metric = tex->distance; + + const char *name = gpu_shader_get_name(tex->feature, tex->dimensions); + + return GPU_stack_link(mat, node, name, in, out, GPU_constant(&metric)); +} + +static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *inVectorSock = nodeFindSocket(node, SOCK_IN, "Vector"); + bNodeSocket *inWSock = nodeFindSocket(node, SOCK_IN, "W"); + bNodeSocket *inSmoothnessSock = nodeFindSocket(node, SOCK_IN, "Smoothness"); + bNodeSocket *inExponentSock = nodeFindSocket(node, SOCK_IN, "Exponent"); + + bNodeSocket *outDistanceSock = nodeFindSocket(node, SOCK_OUT, "Distance"); + bNodeSocket *outColorSock = nodeFindSocket(node, SOCK_OUT, "Color"); + bNodeSocket *outPositionSock = nodeFindSocket(node, SOCK_OUT, "Position"); + bNodeSocket *outWSock = nodeFindSocket(node, SOCK_OUT, "W"); + bNodeSocket *outRadiusSock = nodeFindSocket(node, SOCK_OUT, "Radius"); + + NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage; + + nodeSetSocketAvailability(inWSock, tex->dimensions == 1 || tex->dimensions == 4); + nodeSetSocketAvailability(inVectorSock, tex->dimensions != 1); + nodeSetSocketAvailability( + inExponentSock, + tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && + !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); + nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); + nodeSetSocketAvailability(outColorSock, + tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && + tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); + nodeSetSocketAvailability(outPositionSock, + tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && + tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && + tex->dimensions != 1); + nodeSetSocketAvailability(outWSock, + tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && + tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && + (tex->dimensions == 1 || tex->dimensions == 4)); + nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); +} + +void register_node_type_sh_tex_voronoi(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); + node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out); + node_type_init(&ntype, node_shader_init_tex_voronoi); + node_type_storage( + &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); + node_type_update(&ntype, node_shader_update_tex_voronoi); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c deleted file mode 100644 index 60a3392c761..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** WHITE NOISE **************** */ - -static bNodeSocketTemplate sh_node_tex_white_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; - -static bNodeSocketTemplate sh_node_tex_white_noise_out[] = { - {SOCK_FLOAT, N_("Value")}, - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; - -static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) -{ - node->custom1 = 3; -} - -static int gpu_shader_tex_white_noise(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - static const char *names[] = { - "", - "node_white_noise_1d", - "node_white_noise_2d", - "node_white_noise_3d", - "node_white_noise_4d", - }; - - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - return GPU_stack_link(mat, node, names[node->custom1], in, out); - } - - return 0; -} - -static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); - bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); - - nodeSetSocketAvailability(sockVector, node->custom1 != 1); - nodeSetSocketAvailability(sockW, node->custom1 == 1 || node->custom1 == 4); -} - -void register_node_type_sh_tex_white_noise(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_white_noise_in, sh_node_tex_white_noise_out); - node_type_init(&ntype, node_shader_init_tex_white_noise); - node_type_gpu(&ntype, gpu_shader_tex_white_noise); - node_type_update(&ntype, node_shader_update_tex_white_noise); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc new file mode 100644 index 00000000000..6e973189065 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -0,0 +1,79 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +/* **************** WHITE NOISE **************** */ + +static bNodeSocketTemplate sh_node_tex_white_noise_in[] = { + {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, + {-1, ""}}; + +static bNodeSocketTemplate sh_node_tex_white_noise_out[] = { + {SOCK_FLOAT, N_("Value")}, + {SOCK_RGBA, N_("Color")}, + {-1, ""}, +}; + +static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = 3; +} + +static const char *gpu_shader_get_name(const int dimensions) +{ + BLI_assert(dimensions >= 1 && dimensions <= 4); + return std::array{"node_white_noise_1d", + "node_white_noise_2d", + "node_white_noise_3d", + "node_white_noise_4d"}[dimensions - 1]; +} + +static int gpu_shader_tex_white_noise(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const char *name = gpu_shader_get_name(node->custom1); + return GPU_stack_link(mat, node, name, in, out); +} + +static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); + bNodeSocket *sockW = nodeFindSocket(node, SOCK_IN, "W"); + + nodeSetSocketAvailability(sockVector, node->custom1 != 1); + nodeSetSocketAvailability(sockW, node->custom1 == 1 || node->custom1 == 4); +} + +void register_node_type_sh_tex_white_noise(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); + node_type_socket_templates(&ntype, sh_node_tex_white_noise_in, sh_node_tex_white_noise_out); + node_type_init(&ntype, node_shader_init_tex_white_noise); + node_type_gpu(&ntype, gpu_shader_tex_white_noise); + node_type_update(&ntype, node_shader_update_tex_white_noise); + + nodeRegisterType(&ntype); +} -- cgit v1.2.3 From 410dc76177af4258f3813d623c3cdaaddf1f0300 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 13 Sep 2021 12:40:10 +0200 Subject: Various UI messages fixes and updates. --- .../scripts/modules/bl_i18n_utils/utils_spell_check.py | 13 ++++++++++++- source/blender/editors/undo/ed_undo.c | 4 ++-- source/blender/makesrna/intern/rna_attribute.c | 16 ++++++++-------- source/blender/makesrna/intern/rna_scene.c | 2 +- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 14fc81821c4..0293d7143ec 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -48,6 +48,7 @@ class SpellChecker: "equi", # equi-angular, etc. "fader", "globbing", + "gridded", "haptics", "hasn", # hasn't "hetero", @@ -64,12 +65,14 @@ class SpellChecker: "mplayer", "ons", # add-ons "pong", # ping pong + "resumable", "scalable", "shadeless", "shouldn", # shouldn't "smoothen", "spacings", "teleport", "teleporting", + "tangency", "vertices", "wasn", # wasn't @@ -173,11 +176,13 @@ class SpellChecker: "precalculate", "precomputing", "prefetch", + "preload", "premultiply", "premultiplied", "prepass", "prepend", - "preprocess", "preprocessing", "preprocessor", + "preprocess", "preprocessing", "preprocessor", "preprocessed", "preseek", + "preselect", "preselected", "promillage", "pushdown", "raytree", @@ -185,8 +190,10 @@ class SpellChecker: "realtime", "reinject", "reinjected", "rekey", + "relink", "remesh", "reprojection", "reproject", "reprojecting", + "resample", "resize", "restpose", "resync", "resynced", @@ -226,6 +233,7 @@ class SpellChecker: "todo", "tradeoff", "un", + "unadjust", "unadjusted", "unassociate", "unassociated", "unbake", "uncheck", @@ -388,6 +396,7 @@ class SpellChecker: "boid", "boids", "ceil", "compressibility", + "coplanar", "curvilinear", "equiangular", "equisolid", @@ -396,6 +405,7 @@ class SpellChecker: "gettext", "hashable", "hotspot", + "hydrostatic", "interocular", "intrinsics", "irradiance", @@ -495,6 +505,7 @@ class SpellChecker: "perlin", "phong", "pinlight", + "posterize", "qi", "radiosity", "raycast", "raycasting", diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 84d5d3b9aae..22064e04e86 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -578,8 +578,8 @@ static bool ed_undo_is_init_poll(bContext *C) * it will be part of the exception when attempting to call undo in background mode. */ CTX_wm_operator_poll_msg_set( C, - "Undo disabled at startup in background-mode. " - "Call `ed.undo_push()` to explicitly initialize the undo-system."); + "Undo disabled at startup in background-mode " + "(call `ed.undo_push()` to explicitly initialize the undo-system)"); return false; } return true; diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index b4ea70c33ab..49e813e6a6c 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -41,8 +41,8 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, - {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"}, - {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point values"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, @@ -54,8 +54,8 @@ const EnumPropertyItem rna_enum_attribute_type_with_auto_items[] = { {CD_PROP_FLOAT, "FLOAT", 0, "Float", "Floating-point value"}, {CD_PROP_INT32, "INT", 0, "Integer", "32-bit integer"}, {CD_PROP_FLOAT3, "FLOAT_VECTOR", 0, "Vector", "3D vector with floating-point values"}, - {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point precisions"}, - {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, + {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Color", "RGBA color with floating-point values"}, + {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit values"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {CD_PROP_FLOAT2, "FLOAT2", 0, "2D Vector", "2D vector with floating-point values"}, @@ -443,7 +443,7 @@ static void rna_def_attribute_float_vector(BlenderRNA *brna) srna = RNA_def_struct(brna, "FloatVectorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float Vector Attribute", "Vector geometry attribute, with floating-point precision"); + srna, "Float Vector Attribute", "Vector geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "FloatVectorAttributeValue"); @@ -479,7 +479,7 @@ static void rna_def_attribute_float_color(BlenderRNA *brna) srna = RNA_def_struct(brna, "FloatColorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float Color Attribute", "Color geometry attribute, with floating-point precision"); + srna, "Float Color Attribute", "Color geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "FloatColorAttributeValue"); @@ -514,7 +514,7 @@ static void rna_def_attribute_byte_color(BlenderRNA *brna) srna = RNA_def_struct(brna, "ByteColorAttribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Byte Color Attribute", "Color geometry attribute, with 8-bit precision"); + srna, "Byte Color Attribute", "Color geometry attribute, with 8-bit values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "ByteColorAttributeValue"); @@ -639,7 +639,7 @@ static void rna_def_attribute_float2(BlenderRNA *brna) srna = RNA_def_struct(brna, "Float2Attribute", "Attribute"); RNA_def_struct_sdna(srna, "CustomDataLayer"); RNA_def_struct_ui_text( - srna, "Float2 Attribute", "2D vector geometry attribute, with floating-point precision"); + srna, "Float2 Attribute", "2D vector geometry attribute, with floating-point values"); prop = RNA_def_property(srna, "data", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "Float2AttributeValue"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0583cdeb1bb..ce7c93a46b3 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2845,7 +2845,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) "IMAGE", ICON_IMAGE_DATA, "Image", - "Strick stroke to the image"}, + "Stick stroke to the image"}, /* Weird, GP_PROJECT_VIEWALIGN is inverted. */ {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view"}, {0, NULL, 0, NULL, NULL}, -- cgit v1.2.3 From b777df8080f221a99a6a87c0937868cd21bf5a2b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 13 Sep 2021 13:08:58 +0200 Subject: Fix: fix equality operator for fields Instead of comparing the referenced field node by pointer, compare the nodes directly instead. This is important because different field nodes might be the same semantically. --- source/blender/functions/FN_field.hh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index 730a8046646..d6259bce435 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -151,12 +151,14 @@ template class GFieldBase { friend bool operator==(const GFieldBase &a, const GFieldBase &b) { - return &*a.node_ == &*b.node_ && a.node_output_index_ == b.node_output_index_; + /* Two nodes can compare equal even when their pointer is not the same. For example, two + * "Position" nodes are the same. */ + return *a.node_ == *b.node_ && a.node_output_index_ == b.node_output_index_; } uint64_t hash() const { - return get_default_hash_2(node_, node_output_index_); + return get_default_hash_2(*node_, node_output_index_); } const fn::CPPType &cpp_type() const -- cgit v1.2.3 From 603ae580ce557eb62cb323302dfcc0be265e7ea7 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 13 Sep 2021 13:13:03 +0200 Subject: Build: show better "make update" error message when in detached HEAD state --- build_files/utils/make_update.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index b901fa56f52..a6399790bc9 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -222,6 +222,10 @@ if __name__ == "__main__": # Test if we are building a specific release version. branch = make_utils.git_branch(args.git_command) + if branch == 'HEAD': + sys.stderr.write('Blender git repository is in detached HEAD state, must be in a branch\n') + sys.exit(1) + tag = make_utils.git_tag(args.git_command) release_version = make_utils.git_branch_release_version(branch, tag) -- cgit v1.2.3 From 4b06420e65040c642d2b0a7a1c9bf7515d3cec0c Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 13 Sep 2021 14:13:58 +0200 Subject: Cleanup: Packedfile don't repeat yourself. Introduced `BKE_packedfile_unpack` that is called from the specialized implementation for Image, Sound, Font, Volume etc. This is in preparation for T91252. --- source/blender/blenkernel/BKE_packedFile.h | 6 ++ source/blender/blenkernel/intern/packedFile.c | 81 ++++++++++++++------------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index c45a0bc857d..9b66c6003d7 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -74,6 +74,12 @@ char *BKE_packedfile_unpack_to_file(struct ReportList *reports, const char *local_name, struct PackedFile *pf, enum ePF_FileStatus how); +char *BKE_packedfile_unpack(struct Main *bmain, + struct ReportList *reports, + struct ID *id, + const char *orig_file_name, + struct PackedFile *pf, + enum ePF_FileStatus how); int BKE_packedfile_unpack_vfont(struct Main *bmain, struct ReportList *reports, struct VFont *vfont, diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 78e7e11c248..baff1bb47cc 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -576,26 +576,42 @@ static void unpack_generate_paths(const char *name, } } +char *BKE_packedfile_unpack(Main *bmain, + ReportList *reports, + ID *id, + const char *orig_file_path, + PackedFile *pf, + enum ePF_FileStatus how) +{ + char localname[FILE_MAX], absname[FILE_MAX]; + char *new_name = NULL; + + if (id != NULL) { + unpack_generate_paths( + orig_file_path, id, absname, localname, sizeof(absname), sizeof(localname)); + new_name = BKE_packedfile_unpack_to_file( + reports, BKE_main_blendfile_path(bmain), absname, localname, pf, how); + } + + return new_name; +} + int BKE_packedfile_unpack_vfont(Main *bmain, ReportList *reports, VFont *vfont, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; + if (vfont) { + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)vfont, vfont->filepath, vfont->packedfile, how); - if (vfont != NULL) { - unpack_generate_paths( - vfont->filepath, (ID *)vfont, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, vfont->packedfile, how); - if (newname != NULL) { + if (new_file_path != NULL) { ret_value = RET_OK; BKE_packedfile_free(vfont->packedfile); vfont->packedfile = NULL; - BLI_strncpy(vfont->filepath, newname, sizeof(vfont->filepath)); - MEM_freeN(newname); + BLI_strncpy(vfont->filepath, new_file_path, sizeof(vfont->filepath)); + MEM_freeN(new_file_path); } } @@ -607,18 +623,14 @@ int BKE_packedfile_unpack_sound(Main *bmain, bSound *sound, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; if (sound != NULL) { - unpack_generate_paths( - sound->filepath, (ID *)sound, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, sound->packedfile, how); - if (newname != NULL) { - BLI_strncpy(sound->filepath, newname, sizeof(sound->filepath)); - MEM_freeN(newname); + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)sound, sound->filepath, sound->packedfile, how); + if (new_file_path != NULL) { + BLI_strncpy(sound->filepath, new_file_path, sizeof(sound->filepath)); + MEM_freeN(new_file_path); BKE_packedfile_free(sound->packedfile); sound->packedfile = NULL; @@ -641,16 +653,11 @@ int BKE_packedfile_unpack_image(Main *bmain, if (ima != NULL) { while (ima->packedfiles.last) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; ImagePackedFile *imapf = ima->packedfiles.last; + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)ima, imapf->filepath, imapf->packedfile, how); - unpack_generate_paths( - imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); - newname = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, imapf->packedfile, how); - - if (newname != NULL) { + if (new_file_path != NULL) { ImageView *iv; ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK; @@ -660,14 +667,14 @@ int BKE_packedfile_unpack_image(Main *bmain, /* update the new corresponding view filepath */ iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)); if (iv) { - BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath)); + BLI_strncpy(iv->filepath, new_file_path, sizeof(imapf->filepath)); } /* keep the new name in the image for non-pack specific reasons */ if (how != PF_REMOVE) { - BLI_strncpy(ima->filepath, newname, sizeof(imapf->filepath)); + BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath)); } - MEM_freeN(newname); + MEM_freeN(new_file_path); } else { ret_value = RET_ERROR; @@ -690,18 +697,14 @@ int BKE_packedfile_unpack_volume(Main *bmain, Volume *volume, enum ePF_FileStatus how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newfilepath; int ret_value = RET_ERROR; if (volume != NULL) { - unpack_generate_paths( - volume->filepath, (ID *)volume, absname, localname, sizeof(absname), sizeof(localname)); - newfilepath = BKE_packedfile_unpack_to_file( - reports, BKE_main_blendfile_path(bmain), absname, localname, volume->packedfile, how); - if (newfilepath != NULL) { - BLI_strncpy(volume->filepath, newfilepath, sizeof(volume->filepath)); - MEM_freeN(newfilepath); + char *new_file_path = BKE_packedfile_unpack( + bmain, reports, (ID *)volume, volume->filepath, volume->packedfile, how); + if (new_file_path != NULL) { + BLI_strncpy(volume->filepath, new_file_path, sizeof(volume->filepath)); + MEM_freeN(new_file_path); BKE_packedfile_free(volume->packedfile); volume->packedfile = NULL; -- cgit v1.2.3 From c9c890f196d4f695a83600edfa7782b914b5315b Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 13 Sep 2021 15:57:17 +0200 Subject: Templates: Set more user preference settings to be swapped * Splash Disable * Show gizmo navigate * ui scale It is a bit arbritrary now. So those are the flags I'm setting up for the Blender 101 project (T90025). Differential Revision: https://developer.blender.org/D12474 --- source/blender/blenkernel/intern/blender.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 97a54f289ee..97f8bddc043 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -362,7 +362,9 @@ void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *use DATA_SWAP(app_flag); /* We could add others. */ - FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT); + FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT | USER_SPLASH_DISABLE | USER_SHOW_GIZMO_NAVIGATE); + + DATA_SWAP(ui_scale); #undef SWAP_TYPELESS #undef DATA_SWAP -- cgit v1.2.3 From 6f52ebba192b8a9c5fc1e211e4968ebb42ea8408 Mon Sep 17 00:00:00 2001 From: Iyad Ahmed Date: Mon, 13 Sep 2021 10:29:24 -0400 Subject: UI: Freestyle UI Upgrade Suggested and funded by [[ https://blendernpr.org/| BNPR ]], this patch aims to update the long not-updated Freestyle UI **Why do the UI upgrade:** - Freestyle UI doesn't match the rest of Blender UI, it was neglected for a long time - The current UI makes Freestyle workflows tedious and distracting **Highlights:** For before/after screenshots see https://developer.blender.org/D10505 Video: https://youtu.be/qaXhuJW_c9U Workflow video (older revision): https://youtu.be/IqbjIq_A800 Doc patch (WIP): https://github.com/bnpr/FreestyleUIUpgrade/blob/main/freestyle-ui-upgrade-docs.diff Reviewed By: #user_interface, Blendify, HooglyBoogly, Severin Differential Revision: https://developer.blender.org/D10505 --- .../scripts/startup/bl_ui/properties_freestyle.py | 1323 +++++++++++++------- .../freestyle/intern/application/AppConfig.h | 4 - .../intern/blender_interface/FRS_freestyle.cpp | 13 +- source/blender/makesdna/DNA_freestyle_types.h | 2 +- source/blender/makesrna/intern/rna_scene.c | 16 +- 5 files changed, 889 insertions(+), 469 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index fd12747e2fa..5ca44bb6555 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -22,7 +22,6 @@ from bpy.types import Menu, Panel, UIList # Render properties - class RenderFreestyleButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -49,20 +48,17 @@ class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel): def draw(self, context): layout = self.layout layout.use_property_split = True - layout.use_property_decorate = False # No animation. - + layout.use_property_decorate = False rd = context.scene.render - layout.active = rd.use_freestyle - - layout.prop(rd, "line_thickness_mode", expand=True) - + layout.row().prop(rd, "line_thickness_mode", expand=True, text="Line Thickness Mode") if rd.line_thickness_mode == 'ABSOLUTE': layout.prop(rd, "line_thickness") # Render layer properties + class ViewLayerFreestyleButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -76,8 +72,12 @@ class ViewLayerFreestyleButtonsPanel: rd = scene.render with_freestyle = bpy.app.build_options.freestyle - return (scene and with_freestyle and rd.use_freestyle and - (context.engine in cls.COMPAT_ENGINES)) + return ( + scene + and with_freestyle + and rd.use_freestyle + and (context.engine in cls.COMPAT_ENGINES) + ) class ViewLayerFreestyleEditorButtonsPanel(ViewLayerFreestyleButtonsPanel): @@ -88,7 +88,35 @@ class ViewLayerFreestyleEditorButtonsPanel(ViewLayerFreestyleButtonsPanel): if not super().poll(context): return False view_layer = context.view_layer - return view_layer and view_layer.freestyle_settings.mode == 'EDITOR' + return ( + view_layer + and view_layer.use_freestyle + and view_layer.freestyle_settings.mode == 'EDITOR' + ) + + +class ViewLayerFreestyleLineStyle(ViewLayerFreestyleEditorButtonsPanel): + # Freestyle Linestyle Panels + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + if lineset is None: + return False + linestyle = lineset.linestyle + if linestyle is None: + return False + + return True + + +class ViewLayerFreestyleLinestyleStrokesSubPanel(ViewLayerFreestyleLineStyle): + # Freestyle Linestyle Strokes sub panels + bl_parent_id = "VIEWLAYER_PT_freestyle_linestyle_strokes" class VIEWLAYER_UL_linesets(UIList): @@ -126,54 +154,85 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False view_layer = context.view_layer freestyle = view_layer.freestyle_settings layout.active = view_layer.use_freestyle - row = layout.row() - layout.prop(freestyle, "mode", text="Control Mode") - layout.prop(freestyle, "use_view_map_cache", text="View Map Cache") - layout.prop(freestyle, "as_render_pass", text="As Render Pass") - layout.label(text="Edge Detection Options:") + col = layout.column(align=True) + col.prop(freestyle, "mode", text="Control Mode") + col.prop(freestyle, "use_view_map_cache", text="View Map Cache") + col.prop(freestyle, "as_render_pass", text="As Render Pass") + + +class VIEWLAYER_PT_freestyle_edge_detection_options(ViewLayerFreestyleButtonsPanel, Panel): + bl_label = "Edge Detection Options" + bl_parent_id = "VIEWLAYER_PT_freestyle" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False - split = layout.split() + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + + layout.active = view_layer.use_freestyle - col = split.column() + col = layout.column() col.prop(freestyle, "crease_angle") col.prop(freestyle, "use_culling") - col.prop(freestyle, "use_advanced_options") - - col = split.column() col.prop(freestyle, "use_smoothness") + if freestyle.mode == 'SCRIPT': col.prop(freestyle, "use_material_boundaries") - # Advanced options are hidden by default to warn new users - if freestyle.use_advanced_options: - if freestyle.mode == 'SCRIPT': - row = layout.row() - row.prop(freestyle, "use_ridges_and_valleys") - row.prop(freestyle, "use_suggestive_contours") - row = layout.row() - row.prop(freestyle, "sphere_radius") - row.prop(freestyle, "kr_derivative_epsilon") - if freestyle.mode == 'SCRIPT': - row = layout.row() - row.label(text="Style Modules:") - row.operator("scene.freestyle_module_add", text="Add") - for module in freestyle.modules: - box = layout.box() - box.context_pointer_set("freestyle_module", module) - row = box.row(align=True) - row.prop(module, "use", text="") - row.prop(module, "script", text="") - row.operator("scene.freestyle_module_open", icon='FILEBROWSER', text="") - row.operator("scene.freestyle_module_remove", icon='X', text="") - row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP' - row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + col.prop(freestyle, "use_ridges_and_valleys") + col.prop(freestyle, "use_suggestive_contours") + col.prop(freestyle, "sphere_radius") + col.prop(freestyle, "kr_derivative_epsilon") + + +class VIEWLAYER_PT_freestyle_style_modules(ViewLayerFreestyleButtonsPanel, Panel): + bl_label = "Style Modules" + bl_parent_id = "VIEWLAYER_PT_freestyle" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + return freestyle.mode == 'SCRIPT' + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + + layout.active = view_layer.use_freestyle + + col = layout.column() + col.use_property_split = False + row = col.row() + row.operator("scene.freestyle_module_add", text="Add") + for module in freestyle.modules: + box = col.box() + box.context_pointer_set("freestyle_module", module) + row = box.row(align=True) + row.prop(module, "use", text="") + row.prop(module, "script", text="") + row.operator("scene.freestyle_module_open", icon='FILEBROWSER', text="") + row.operator("scene.freestyle_module_remove", icon='X', text="") + row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP' + row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN' class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel): @@ -198,10 +257,13 @@ class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel freestyle = view_layer.freestyle_settings lineset = freestyle.linesets.active - layout.active = view_layer.use_freestyle - row = layout.row() - rows = 4 if lineset else 2 + + is_sortable = len(freestyle.linesets) > 1 + rows = 3 + if is_sortable: + rows = 5 + row.template_list( "VIEWLAYER_UL_linesets", "", @@ -212,157 +274,504 @@ class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel rows=rows, ) - sub = row.column(align=True) - sub.operator("scene.freestyle_lineset_add", icon='ADD', text="") - sub.operator("scene.freestyle_lineset_remove", icon='REMOVE', text="") - sub.menu("RENDER_MT_lineset_context_menu", icon='DOWNARROW_HLT', text="") + col = row.column(align=True) + col.operator("scene.freestyle_lineset_add", icon='ADD', text="") + col.operator("scene.freestyle_lineset_remove", icon='REMOVE', text="") + + col.separator() + + col.menu("RENDER_MT_lineset_context_menu", icon="DOWNARROW_HLT", text="") + + if is_sortable: + col.separator() + col.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP' + col.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + if lineset: - sub.separator() - sub.separator() - sub.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP' - sub.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN' - - col = layout.column() - col.label(text="Selection By:") - row = col.row(align=True) - row.prop(lineset, "select_by_visibility", text="Visibility", toggle=True) - row.prop(lineset, "select_by_edge_types", text="Edge Types", toggle=True) - row.prop(lineset, "select_by_face_marks", text="Face Marks", toggle=True) - row.prop(lineset, "select_by_collection", text="Collection", toggle=True) - row.prop(lineset, "select_by_image_border", text="Image Border", toggle=True) - - if lineset.select_by_visibility: - col.label(text="Visibility:") - row = col.row(align=True) - row.prop(lineset, "visibility", expand=True) - if lineset.visibility == 'RANGE': - row = col.row(align=True) - row.prop(lineset, "qi_start") - row.prop(lineset, "qi_end") - - if lineset.select_by_edge_types: - col.label(text="Edge Types:") - row = col.row() - row.prop(lineset, "edge_type_negation", expand=True) - row.prop(lineset, "edge_type_combination", expand=True) - - split = col.split() - - sub = split.column() - self.draw_edge_type_buttons(sub, lineset, "silhouette") - self.draw_edge_type_buttons(sub, lineset, "border") - self.draw_edge_type_buttons(sub, lineset, "contour") - self.draw_edge_type_buttons(sub, lineset, "suggestive_contour") - self.draw_edge_type_buttons(sub, lineset, "ridge_valley") - - sub = split.column() - self.draw_edge_type_buttons(sub, lineset, "crease") - self.draw_edge_type_buttons(sub, lineset, "edge_mark") - self.draw_edge_type_buttons(sub, lineset, "external_contour") - self.draw_edge_type_buttons(sub, lineset, "material_boundary") - - if lineset.select_by_face_marks: - col.label(text="Face Marks:") - row = col.row() - row.prop(lineset, "face_mark_negation", expand=True) - row.prop(lineset, "face_mark_condition", expand=True) - - if lineset.select_by_collection: - col.label(text="Collection:") - row = col.row() - row.prop(lineset, "collection", text="") - row.prop(lineset, "collection_negation", expand=True) - - -class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Panel): - bl_label = "Freestyle Line Style" + layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new") + layout.separator() + col = layout.column(heading="Select by") + col.use_property_split = True + col.use_property_decorate = False + col.prop(lineset, "select_by_image_border", text="Image Border") + + +# Freestyle Lineset Sub Panels +class VIEWLAYER_PT_freestyle_lineset_visibilty(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Visibility" + bl_parent_id = "VIEWLAYER_PT_freestyle_lineset" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.prop(lineset, "select_by_visibility", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.active = lineset.select_by_visibility + col = layout.column(align=True, heading="Type") + col.prop(lineset, "visibility", text="Type", expand=False) + + if lineset.visibility == 'RANGE': + col = layout.column(align=True) + col.use_property_split = True + col.prop(lineset, "qi_start") + col.prop(lineset, "qi_end") + + +class VIEWLAYER_PT_freestyle_lineset_edgetype(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Edge Type" + bl_parent_id = "VIEWLAYER_PT_freestyle_lineset" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.prop(lineset, "select_by_edge_types", text="") + + def draw_edge_type_buttons(self, box, lineset, edge_type): + # property names + select_edge_type = "select_" + edge_type + exclude_edge_type = "exclude_" + edge_type + # draw edge type buttons + row = box.row(align=True) + row.prop(lineset, select_edge_type) + sub = row.column(align=True) + sub.prop(lineset, exclude_edge_type, text="") + sub.active = getattr(lineset, select_edge_type) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.active = lineset.select_by_edge_types + layout.row().prop(lineset, "edge_type_negation", expand=True, text="Negation") + layout.row().prop(lineset, "edge_type_combination", expand=True, text="Combination") + + col = layout.column(heading="Type") + self.draw_edge_type_buttons(col, lineset, "silhouette") + self.draw_edge_type_buttons(col, lineset, "crease") + self.draw_edge_type_buttons(col, lineset, "border") + self.draw_edge_type_buttons(col, lineset, "edge_mark") + self.draw_edge_type_buttons(col, lineset, "contour") + self.draw_edge_type_buttons(col, lineset, "external_contour") + self.draw_edge_type_buttons(col, lineset, "material_boundary") + self.draw_edge_type_buttons(col, lineset, "suggestive_contour") + self.draw_edge_type_buttons(col, lineset, "ridge_valley") + + +class VIEWLAYER_PT_freestyle_lineset_facemarks(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Face Marks" + bl_parent_id = "VIEWLAYER_PT_freestyle_lineset" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.prop(lineset, "select_by_face_marks", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.active = lineset.select_by_face_marks + layout.row().prop(lineset, "face_mark_negation", expand=True, text="Negation") + layout.row().prop(lineset, "face_mark_condition", expand=True, text="Condition") + + +class VIEWLAYER_PT_freestyle_lineset_collection(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Collection" + bl_parent_id = "VIEWLAYER_PT_freestyle_lineset" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + bl_options = {'DEFAULT_CLOSED'} - def draw_modifier_box_header(self, box, modifier): - row = box.row() - row.context_pointer_set("modifier", modifier) - if modifier.expanded: - icon = 'TRIA_DOWN' - else: - icon = 'TRIA_RIGHT' - row.prop(modifier, "expanded", text="", icon=icon, emboss=False) - # TODO: Use icons rather than text label, would save some room! - row.label(text=modifier.rna_type.name) - row.prop(modifier, "name", text="") - if modifier.use: - icon = 'RESTRICT_RENDER_OFF' - else: - icon = 'RESTRICT_RENDER_ON' - row.prop(modifier, "use", text="", icon=icon) - sub = row.row(align=True) - sub.operator("scene.freestyle_modifier_copy", icon='NONE', text="Copy") - sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP' - sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN' - sub.operator("scene.freestyle_modifier_remove", icon='X', text="") - - def draw_modifier_box_error(self, box, _modifier, message): - row = box.row() - row.label(text=message, icon='ERROR') - - def draw_modifier_common(self, box, modifier): - row = box.row() - row.prop(modifier, "blend", text="") - row.prop(modifier, "influence") - - def draw_modifier_color_ramp_common(self, box, modifier, has_range): - box.template_color_ramp(modifier, "color_ramp", expand=True) - if has_range: - row = box.row(align=True) - row.prop(modifier, "range_min") - row.prop(modifier, "range_max") - - def draw_modifier_curve_common(self, box, modifier, has_range, has_value): - row = box.row() - row.prop(modifier, "mapping", text="") - sub = row.column() - sub.prop(modifier, "invert") - if modifier.mapping == 'CURVE': - sub.active = False - box.template_curve_mapping(modifier, "curve") - if has_range: - row = box.row(align=True) - row.prop(modifier, "range_min") - row.prop(modifier, "range_max") - if has_value: - row = box.row(align=True) - row.prop(modifier, "value_min") - row.prop(modifier, "value_max") + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.prop(lineset, "select_by_collection", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + layout.active = lineset.select_by_collection + layout.row().prop(lineset, "collection", text="Freestyle Lineset Collection") + layout.row().prop(lineset, "collection_negation", expand=True, text="Negation") + + +# Linestyle Modifier Drawing code +def draw_modifier_box_header(box, modifier): + row = box.row() + row.use_property_split = False + row.context_pointer_set("modifier", modifier) + if modifier.expanded: + icon = 'TRIA_DOWN' + else: + icon = 'TRIA_RIGHT' + row.prop(modifier, "expanded", text="", icon=icon, emboss=False) + + sub = row.row(align=True) + sub.prop(modifier, "name", text="") + if modifier.use: + icon = 'RESTRICT_RENDER_OFF' + else: + icon = 'RESTRICT_RENDER_ON' + sub.prop(modifier, "use", text="", icon=icon) + sub.operator("scene.freestyle_modifier_copy", icon='DUPLICATE', text="") + + sub = row.row(align=True) + sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP' + sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + + row.operator("scene.freestyle_modifier_remove", icon='X', text="", emboss=False) + + +def draw_modifier_box_error(box, _modifier, message): + row = box.row() + row.label(text=message, icon='ERROR') + + +def draw_modifier_common(box, modifier): + col = box.column() + col.prop(modifier, "blend", text="Blend Mode") + col.prop(modifier, "influence") + + +def draw_modifier_color_ramp_common(box, modifier, has_range): + box.template_color_ramp(modifier, "color_ramp", expand=True) + if has_range: + col = box.column(align=True) + col.prop(modifier, "range_min", text="Range Min") + col.prop(modifier, "range_max", text="Max") + + +def draw_modifier_curve_common(box, modifier, has_range, has_value): + row = box.row() + row.prop(modifier, "mapping", text="Mapping") + if modifier.mapping == 'LINEAR': + box.prop(modifier, "invert") + if has_range: + col = box.column(align=True) + col.prop(modifier, "range_min", text="Range Min") + col.prop(modifier, "range_max", text="Max") + if has_value: + col = box.column(align=True) + col.prop(modifier, "value_min", text="Value Min") + col.prop(modifier, "value_max", text="Max") + if modifier.mapping == 'CURVE': + box.template_curve_mapping(modifier, "curve") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Strokes" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + + layout.active = view_layer.use_freestyle + + if lineset is None: + return + linestyle = lineset.linestyle + + if linestyle is None: + return + + row = layout.row(align=True) + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) + + col = layout.column(align=True) + col.prop(linestyle, "caps", expand=False) + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_chaining(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Chaining" + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + linestyle = lineset.linestyle + layout.prop(linestyle, "use_chaining", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + linestyle = lineset.linestyle + + layout.active = linestyle.use_chaining + layout.row().prop(linestyle, "chaining", expand=True, text="Method") + if linestyle.chaining == 'SKETCHY': + layout.prop(linestyle, "rounds") + layout.prop(linestyle, "use_same_object") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_splitting(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Splitting" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + linestyle = lineset.linestyle + + row = layout.row(align=False, heading="Min 2D Angle") + row.prop(linestyle, "use_angle_min", text="") + sub = row.row() + sub.active = linestyle.use_angle_min + sub.prop(linestyle, "angle_min", text="") + + row = layout.row(align=False, heading="Max 2D Angle") + row.prop(linestyle, "use_angle_max", text="") + sub = row.row() + sub.active = linestyle.use_angle_max + sub.prop(linestyle, "angle_max", text="") + + row = layout.row(align=False, heading="2D Length") + row.prop(linestyle, "use_split_length", text="") + sub = row.row() + sub.active = linestyle.use_split_length + sub.prop(linestyle, "split_length", text="") + + layout.prop(linestyle, "material_boundary") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_splitting_pattern(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Split Pattern" + bl_parent_id = "VIEWLAYER_PT_freestyle_linestyle_strokes_splitting" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + linestyle = lineset.linestyle + layout.prop(linestyle, "use_split_pattern", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + linestyle = lineset.linestyle + + layout.active = linestyle.use_split_pattern + + col = layout.column(align=True) + col.prop(linestyle, "split_dash1", text="Dash 1") + col.prop(linestyle, "split_dash2", text="2") + col.prop(linestyle, "split_dash3", text="3") + col = layout.column(align=True) + col.prop(linestyle, "split_gap1", text="Gap 1") + col.prop(linestyle, "split_gap2", text="2") + col.prop(linestyle, "split_gap3", text="3") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_sorting(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Sorting" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + linestyle = lineset.linestyle + layout.prop(linestyle, "use_sorting", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + linestyle = lineset.linestyle + + layout.active = linestyle.use_sorting + + layout.prop(linestyle, "sort_key") + + row = layout.row() + row.active = linestyle.sort_key in { + 'DISTANCE_FROM_CAMERA', + 'PROJECTED_X', + 'PROJECTED_Y', + } + row.prop(linestyle, "integration_type") + layout.row().prop(linestyle, "sort_order", expand=True, text="Sort Order") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_selection(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Selection" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + linestyle = lineset.linestyle + + row = layout.row(align=False, heading="Min 2D Length") + row.prop(linestyle, "use_length_min", text="") + sub = row.row() + sub.active = linestyle.use_length_min + sub.prop(linestyle, "length_min", text="") + + row = layout.row(align=False, heading="Max 2D Length") + row.prop(linestyle, "use_length_max", text="") + sub = row.row() + sub.active = linestyle.use_length_max + sub.prop(linestyle, "length_max", text="") + + row = layout.row(align=False, heading="Chain Count") + row.prop(linestyle, "use_chain_count", text="") + sub = row.row() + sub.active = linestyle.use_chain_count + sub.prop(linestyle, "chain_count", text="") + + +class VIEWLAYER_PT_freestyle_linestyle_strokes_dashedline(ViewLayerFreestyleLinestyleStrokesSubPanel, Panel): + bl_label = "Dashed Line" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + layout = self.layout + + view_layer = context.view_layer + freestyle = view_layer.freestyle_settings + lineset = freestyle.linesets.active + + linestyle = lineset.linestyle + layout.prop(linestyle, "use_dashed_line", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + linestyle = lineset.linestyle + + layout.active = linestyle.use_dashed_line + + col = layout.column(align=True) + col.prop(linestyle, "dash1", text="Dash 1") + col.prop(linestyle, "dash2", text="2") + col.prop(linestyle, "dash3", text="3") + col = layout.column(align=True) + col.prop(linestyle, "gap1", text="Gap 1") + col.prop(linestyle, "gap2", text="2") + col.prop(linestyle, "gap3", text="3") + + +class VIEWLAYER_PT_freestyle_linestyle_color(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Color" + bl_options = {'DEFAULT_CLOSED'} def draw_color_modifier(self, context, modifier): layout = self.layout col = layout.column(align=True) - self.draw_modifier_box_header(col.box(), modifier) + draw_modifier_box_header(col.box(), modifier) if modifier.expanded: box = col.box() - self.draw_modifier_common(box, modifier) + draw_modifier_common(box, modifier) if modifier.type == 'ALONG_STROKE': - self.draw_modifier_color_ramp_common(box, modifier, False) + draw_modifier_color_ramp_common(box, modifier, False) elif modifier.type == 'DISTANCE_FROM_OBJECT': box.prop(modifier, "target") - self.draw_modifier_color_ramp_common(box, modifier, True) + draw_modifier_color_ramp_common(box, modifier, True) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'COLOR' prop.name = modifier.name elif modifier.type == 'DISTANCE_FROM_CAMERA': - self.draw_modifier_color_ramp_common(box, modifier, True) + draw_modifier_color_ramp_common(box, modifier, True) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'COLOR' prop.name = modifier.name elif modifier.type == 'MATERIAL': row = box.row() - row.prop(modifier, "material_attribute", text="") - sub = row.column() + row.prop(modifier, "material_attribute", + text="Material Attribute") + sub = box.column() sub.prop(modifier, "use_ramp") if modifier.material_attribute in {'LINE', 'DIFF', 'SPEC'}: sub.active = True @@ -371,166 +780,292 @@ class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Pan sub.active = False show_ramp = True if show_ramp: - self.draw_modifier_color_ramp_common(box, modifier, False) + draw_modifier_color_ramp_common(box, modifier, False) elif modifier.type == 'TANGENT': - self.draw_modifier_color_ramp_common(box, modifier, False) + draw_modifier_color_ramp_common(box, modifier, False) elif modifier.type == 'NOISE': - self.draw_modifier_color_ramp_common(box, modifier, False) - row = box.row(align=False) - row.prop(modifier, "amplitude") - row.prop(modifier, "period") - row.prop(modifier, "seed") + subcol = box.column(align=True) + subcol.prop(modifier, "amplitude") + subcol.prop(modifier, "period") + subcol.prop(modifier, "seed") + draw_modifier_color_ramp_common(box, modifier, False) elif modifier.type == 'CREASE_ANGLE': - self.draw_modifier_color_ramp_common(box, modifier, False) - row = box.row(align=True) - row.prop(modifier, "angle_min") - row.prop(modifier, "angle_max") + subcol = box.column(align=True) + subcol.prop(modifier, "angle_min", text="Angle Min") + subcol.prop(modifier, "angle_max", text="Max") + draw_modifier_color_ramp_common(box, modifier, False) elif modifier.type == 'CURVATURE_3D': - self.draw_modifier_color_ramp_common(box, modifier, False) - row = box.row(align=True) - row.prop(modifier, "curvature_min") - row.prop(modifier, "curvature_max") + subcol = box.column(align=True) + subcol.prop(modifier, "curvature_min", text="Curvature Min") + subcol.prop(modifier, "curvature_max", text="Max") + + draw_modifier_color_ramp_common(box, modifier, False) + freestyle = context.view_layer.freestyle_settings if not freestyle.use_smoothness: message = "Enable Face Smoothness to use this modifier" - self.draw_modifier_box_error(col.box(), modifier, message) + draw_modifier_box_error(col.box(), modifier, message) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + + layout.active = view_layer.use_freestyle + + if lineset is None: + return + linestyle = lineset.linestyle + + if linestyle is None: + return + + row = layout.row(align=True) + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) + + col = layout.column() + row = col.row() + row.prop(linestyle, "color", text="Base Color") + col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier") + for modifier in linestyle.color_modifiers: + self.draw_color_modifier(context, modifier) + + +class VIEWLAYER_PT_freestyle_linestyle_alpha(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Alpha" + bl_options = {'DEFAULT_CLOSED'} def draw_alpha_modifier(self, context, modifier): layout = self.layout col = layout.column(align=True) - self.draw_modifier_box_header(col.box(), modifier) + draw_modifier_box_header(col.box(), modifier) if modifier.expanded: box = col.box() - self.draw_modifier_common(box, modifier) + draw_modifier_common(box, modifier) if modifier.type == 'ALONG_STROKE': - self.draw_modifier_curve_common(box, modifier, False, False) + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'DISTANCE_FROM_OBJECT': box.prop(modifier, "target") - self.draw_modifier_curve_common(box, modifier, True, False) + draw_modifier_curve_common(box, modifier, True, False) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'ALPHA' prop.name = modifier.name elif modifier.type == 'DISTANCE_FROM_CAMERA': - self.draw_modifier_curve_common(box, modifier, True, False) + draw_modifier_curve_common(box, modifier, True, False) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'ALPHA' prop.name = modifier.name elif modifier.type == 'MATERIAL': - box.prop(modifier, "material_attribute", text="") - self.draw_modifier_curve_common(box, modifier, False, False) + box.prop(modifier, "material_attribute", text="Material Attribute") + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'TANGENT': - self.draw_modifier_curve_common(box, modifier, False, False) + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'NOISE': - self.draw_modifier_curve_common(box, modifier, False, False) - row = box.row(align=False) - row.prop(modifier, "amplitude") - row.prop(modifier, "period") - row.prop(modifier, "seed") + col = box.column(align=True) + col.prop(modifier, "amplitude") + col.prop(modifier, "period") + col.prop(modifier, "seed") + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'CREASE_ANGLE': - self.draw_modifier_curve_common(box, modifier, False, False) - row = box.row(align=True) - row.prop(modifier, "angle_min") - row.prop(modifier, "angle_max") + col = box.column(align=True) + col.prop(modifier, "angle_min", text="Angle Min") + col.prop(modifier, "angle_max", text="Max") + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'CURVATURE_3D': - self.draw_modifier_curve_common(box, modifier, False, False) - row = box.row(align=True) - row.prop(modifier, "curvature_min") - row.prop(modifier, "curvature_max") + draw_modifier_curve_common(box, modifier, False, False) + + subcol = box.column(align=True) + subcol.prop(modifier, "curvature_min", text="Curvature Min") + subcol.prop(modifier, "curvature_max", text="Max") + freestyle = context.view_layer.freestyle_settings if not freestyle.use_smoothness: message = "Enable Face Smoothness to use this modifier" - self.draw_modifier_box_error(col.box(), modifier, message) + draw_modifier_box_error(col.box(), modifier, message) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + + layout.active = view_layer.use_freestyle + + if lineset is None: + return + linestyle = lineset.linestyle + + if linestyle is None: + return + + row = layout.row(align=True) + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) + + col = layout.column() + row = col.row() + row.prop(linestyle, "alpha", text="Base Transparency") + col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier") + for modifier in linestyle.alpha_modifiers: + self.draw_alpha_modifier(context, modifier) + + +class VIEWLAYER_PT_freestyle_linestyle_thickness(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Thickness" + bl_options = {'DEFAULT_CLOSED'} def draw_thickness_modifier(self, context, modifier): layout = self.layout col = layout.column(align=True) - self.draw_modifier_box_header(col.box(), modifier) + draw_modifier_box_header(col.box(), modifier) if modifier.expanded: box = col.box() - self.draw_modifier_common(box, modifier) + draw_modifier_common(box, modifier) if modifier.type == 'ALONG_STROKE': - self.draw_modifier_curve_common(box, modifier, False, True) + draw_modifier_curve_common(box, modifier, False, True) elif modifier.type == 'DISTANCE_FROM_OBJECT': box.prop(modifier, "target") - self.draw_modifier_curve_common(box, modifier, True, True) + draw_modifier_curve_common(box, modifier, True, True) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'THICKNESS' prop.name = modifier.name elif modifier.type == 'DISTANCE_FROM_CAMERA': - self.draw_modifier_curve_common(box, modifier, True, True) + draw_modifier_curve_common(box, modifier, True, True) prop = box.operator("scene.freestyle_fill_range_by_selection") prop.type = 'THICKNESS' prop.name = modifier.name elif modifier.type == 'MATERIAL': - box.prop(modifier, "material_attribute", text="") - self.draw_modifier_curve_common(box, modifier, False, True) + box.prop(modifier, "material_attribute", text="Material Attribute") + draw_modifier_curve_common(box, modifier, False, True) elif modifier.type == 'CALLIGRAPHY': box.prop(modifier, "orientation") - row = box.row(align=True) - row.prop(modifier, "thickness_min") - row.prop(modifier, "thickness_max") + subcol = box.column(align=True) + subcol.prop(modifier, "thickness_min", text="Thickness Min") + subcol.prop(modifier, "thickness_max", text="Max") elif modifier.type == 'TANGENT': - self.draw_modifier_curve_common(box, modifier, False, False) self.mapping = 'CURVE' - row = box.row(align=True) - row.prop(modifier, "thickness_min") - row.prop(modifier, "thickness_max") + subcol = box.column(align=True) + subcol.prop(modifier, "thickness_min", text="Thickness Min") + subcol.prop(modifier, "thickness_max", text="Max") + + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'NOISE': - row = box.row(align=False) - row.prop(modifier, "amplitude") - row.prop(modifier, "period") - row = box.row(align=False) - row.prop(modifier, "seed") - row.prop(modifier, "use_asymmetric") + col = box.column(align=True) + col.prop(modifier, "amplitude") + col.prop(modifier, "period") + + col = box.column(align=True) + col.prop(modifier, "seed") + col.prop(modifier, "use_asymmetric") elif modifier.type == 'CREASE_ANGLE': - self.draw_modifier_curve_common(box, modifier, False, False) - row = box.row(align=True) - row.prop(modifier, "thickness_min") - row.prop(modifier, "thickness_max") - row = box.row(align=True) - row.prop(modifier, "angle_min") - row.prop(modifier, "angle_max") + col = box.column(align=True) + col.prop(modifier, "thickness_min", text="Thickness Min") + col.prop(modifier, "thickness_max", text="Max") + + col = box.column(align=True) + col.prop(modifier, "angle_min", text="Angle Min") + col.prop(modifier, "angle_max", text="Max") + + draw_modifier_curve_common(box, modifier, False, False) elif modifier.type == 'CURVATURE_3D': - self.draw_modifier_curve_common(box, modifier, False, False) - row = box.row(align=True) - row.prop(modifier, "thickness_min") - row.prop(modifier, "thickness_max") - row = box.row(align=True) - row.prop(modifier, "curvature_min") - row.prop(modifier, "curvature_max") + subcol = box.column(align=True) + subcol.prop(modifier, "thickness_min", text="Thickness Min") + subcol.prop(modifier, "thickness_max", text="Max") + + subcol = box.column(align=True) + subcol.prop(modifier, "curvature_min") + subcol.prop(modifier, "curvature_max") + + draw_modifier_curve_common(box, modifier, False, False) + freestyle = context.view_layer.freestyle_settings if not freestyle.use_smoothness: message = "Enable Face Smoothness to use this modifier" - self.draw_modifier_box_error(col.box(), modifier, message) + draw_modifier_box_error(col.box(), modifier, message) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + + layout.active = view_layer.use_freestyle + + if lineset is None: + return + linestyle = lineset.linestyle + + if linestyle is None: + return + + row = layout.row(align=True) + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) + + col = layout.column() + row = col.row() + row.prop(linestyle, "thickness", text="Base Thickness") + subcol = col.column() + subcol.active = linestyle.chaining == 'PLAIN' and linestyle.use_same_object + row = subcol.row() + row.prop(linestyle, "thickness_position", expand=False) + + if linestyle.thickness_position == 'RELATIVE': + row = subcol.row() + row.prop(linestyle, "thickness_ratio") + + col = layout.column() + col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier") + for modifier in linestyle.thickness_modifiers: + self.draw_thickness_modifier(context, modifier) + + +class VIEWLAYER_PT_freestyle_linestyle_geometry(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Geometry" + bl_options = {'DEFAULT_CLOSED'} def draw_geometry_modifier(self, _context, modifier): layout = self.layout col = layout.column(align=True) - self.draw_modifier_box_header(col.box(), modifier) + draw_modifier_box_header(col.box(), modifier) if modifier.expanded: box = col.box() @@ -541,40 +1076,40 @@ class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Pan box.prop(modifier, "error") elif modifier.type == 'SINUS_DISPLACEMENT': - split = box.split() - col = split.column() + col = box.column(align=True) col.prop(modifier, "wavelength") col.prop(modifier, "amplitude") - col = split.column() + + col = box.column(align=True) col.prop(modifier, "phase") elif modifier.type == 'SPATIAL_NOISE': - split = box.split() - col = split.column() + col = box.column(align=True) col.prop(modifier, "amplitude") col.prop(modifier, "scale") col.prop(modifier, "octaves") - col = split.column() + + col = box.column(align=True) col.prop(modifier, "smooth") col.prop(modifier, "use_pure_random") elif modifier.type == 'PERLIN_NOISE_1D': - split = box.split() - col = split.column() + col = box.column(align=True) col.prop(modifier, "frequency") col.prop(modifier, "amplitude") col.prop(modifier, "seed") - col = split.column() + + col = box.column(align=True) col.prop(modifier, "octaves") col.prop(modifier, "angle") elif modifier.type == 'PERLIN_NOISE_2D': - split = box.split() - col = split.column() + col = box.column(align=True) col.prop(modifier, "frequency") col.prop(modifier, "amplitude") col.prop(modifier, "seed") - col = split.column() + + col = box.column(align=True) col.prop(modifier, "octaves") col.prop(modifier, "angle") @@ -594,33 +1129,34 @@ class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Pan row = box.row() row.prop(modifier, "shape", expand=True) box.prop(modifier, "rounds") - row = box.row() + subcol = box.column(align=True) if modifier.shape in {'CIRCLES', 'ELLIPSES'}: - row.prop(modifier, "random_radius") - row.prop(modifier, "random_center") + subcol.prop(modifier, "random_radius", text="Random Radius") + subcol.prop(modifier, "random_center", text="Center") elif modifier.shape == 'SQUARES': - row.prop(modifier, "backbone_length") - row.prop(modifier, "random_backbone") + subcol.prop(modifier, "backbone_length", text="Backbone Length") + subcol.prop(modifier, "random_backbone", text="Randomness") elif modifier.type == '2D_OFFSET': - row = box.row(align=True) - row.prop(modifier, "start") - row.prop(modifier, "end") - row = box.row(align=True) - row.prop(modifier, "x") - row.prop(modifier, "y") + subcol = box.column(align=True) + subcol.prop(modifier, "start") + subcol.prop(modifier, "end") + + subcol = box.column(align=True) + subcol.prop(modifier, "x") + subcol.prop(modifier, "y") elif modifier.type == '2D_TRANSFORM': box.prop(modifier, "pivot") if modifier.pivot == 'PARAM': box.prop(modifier, "pivot_u") elif modifier.pivot == 'ABSOLUTE': - row = box.row(align=True) - row.prop(modifier, "pivot_x") - row.prop(modifier, "pivot_y") - row = box.row(align=True) - row.prop(modifier, "scale_x") - row.prop(modifier, "scale_y") + subcol = box.column(align=True) + subcol.prop(modifier, "pivot_x", text="Pivot X") + subcol.prop(modifier, "pivot_y", text="Y") + subcol = box.column(align=True) + subcol.prop(modifier, "scale_x", text="Scale X") + subcol.prop(modifier, "scale_y", text="Y") box.prop(modifier, "angle") elif modifier.type == 'SIMPLIFICATION': @@ -628,6 +1164,8 @@ class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Pan def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False view_layer = context.view_layer lineset = view_layer.freestyle_settings.linesets.active @@ -638,181 +1176,61 @@ class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Pan return linestyle = lineset.linestyle - layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new") if linestyle is None: return + row = layout.row(align=True) - row.prop(linestyle, "panel", expand=True) - if linestyle.panel == 'STROKES': - # Chaining - layout.prop(linestyle, "use_chaining", text="Chaining:") - split = layout.split(align=True) - split.active = linestyle.use_chaining - # First column - col = split.column() - col.active = linestyle.use_chaining - col.prop(linestyle, "chaining", text="") - if linestyle.chaining == 'SKETCHY': - col.prop(linestyle, "rounds") - # Second column - col = split.column() - col.prop(linestyle, "use_same_object") - - # Splitting - layout.label(text="Splitting:") - split = layout.split(align=True) - # First column - col = split.column() - row = col.row(align=True) - row.prop(linestyle, "use_angle_min", text="") - sub = row.row() - sub.active = linestyle.use_angle_min - sub.prop(linestyle, "angle_min") - row = col.row(align=True) - row.prop(linestyle, "use_angle_max", text="") - sub = row.row() - sub.active = linestyle.use_angle_max - sub.prop(linestyle, "angle_max") - # Second column - col = split.column() - row = col.row(align=True) - row.prop(linestyle, "use_split_length", text="") - sub = row.row() - sub.active = linestyle.use_split_length - sub.prop(linestyle, "split_length", text="2D Length") - row = col.row(align=True) - row.prop(linestyle, "material_boundary") - # End of columns - row = layout.row(align=True) - row.prop(linestyle, "use_split_pattern", text="") - sub = row.row(align=True) - sub.active = linestyle.use_split_pattern - sub.prop(linestyle, "split_dash1", text="D1") - sub.prop(linestyle, "split_gap1", text="G1") - sub.prop(linestyle, "split_dash2", text="D2") - sub.prop(linestyle, "split_gap2", text="G2") - sub.prop(linestyle, "split_dash3", text="D3") - sub.prop(linestyle, "split_gap3", text="G3") - - # Sorting - layout.prop(linestyle, "use_sorting", text="Sorting:") - col = layout.column() - col.active = linestyle.use_sorting - row = col.row(align=True) - row.prop(linestyle, "sort_key", text="") - sub = row.row() - sub.active = linestyle.sort_key in {'DISTANCE_FROM_CAMERA', - 'PROJECTED_X', - 'PROJECTED_Y'} - sub.prop(linestyle, "integration_type", text="") - row = col.row(align=True) - row.prop(linestyle, "sort_order", expand=True) - - # Selection - layout.label(text="Selection:") - split = layout.split(align=True) - # First column - col = split.column() - row = col.row(align=True) - row.prop(linestyle, "use_length_min", text="") - sub = row.row() - sub.active = linestyle.use_length_min - sub.prop(linestyle, "length_min") - row = col.row(align=True) - row.prop(linestyle, "use_length_max", text="") - sub = row.row() - sub.active = linestyle.use_length_max - sub.prop(linestyle, "length_max") - # Second column - col = split.column() - row = col.row(align=True) - row.prop(linestyle, "use_chain_count", text="") - sub = row.row() - sub.active = linestyle.use_chain_count - sub.prop(linestyle, "chain_count") - - # Caps - layout.label(text="Caps:") - row = layout.row(align=True) - row.prop(linestyle, "caps", expand=True) - - # Dashed lines - layout.prop(linestyle, "use_dashed_line", text="Dashed Line:") - row = layout.row(align=True) - row.active = linestyle.use_dashed_line - row.prop(linestyle, "dash1", text="D1") - row.prop(linestyle, "gap1", text="G1") - row.prop(linestyle, "dash2", text="D2") - row.prop(linestyle, "gap2", text="G2") - row.prop(linestyle, "dash3", text="D3") - row.prop(linestyle, "gap3", text="G3") - - elif linestyle.panel == 'COLOR': - col = layout.column() - row = col.row() - row.label(text="Base Color:") - row.prop(linestyle, "color", text="") - col.label(text="Modifiers:") - col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier") - for modifier in linestyle.color_modifiers: - self.draw_color_modifier(context, modifier) - - elif linestyle.panel == 'ALPHA': - col = layout.column() - row = col.row() - row.label(text="Base Transparency:") - row.prop(linestyle, "alpha") - col.label(text="Modifiers:") - col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier") - for modifier in linestyle.alpha_modifiers: - self.draw_alpha_modifier(context, modifier) - - elif linestyle.panel == 'THICKNESS': - col = layout.column() - row = col.row() - row.label(text="Base Thickness:") - row.prop(linestyle, "thickness") - subcol = col.column() - subcol.active = linestyle.chaining == 'PLAIN' and linestyle.use_same_object - row = subcol.row() - row.prop(linestyle, "thickness_position", expand=True) - row = subcol.row() - row.prop(linestyle, "thickness_ratio") - row.active = (linestyle.thickness_position == 'RELATIVE') - col = layout.column() - col.label(text="Modifiers:") - col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier") - for modifier in linestyle.thickness_modifiers: - self.draw_thickness_modifier(context, modifier) - - elif linestyle.panel == 'GEOMETRY': - col = layout.column() - col.label(text="Modifiers:") - col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier") - for modifier in linestyle.geometry_modifiers: - self.draw_geometry_modifier(context, modifier) - - elif linestyle.panel == 'TEXTURE': - layout.separator() + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) - row = layout.row() - row.prop(linestyle, "use_nodes") - row.prop(linestyle, "texture_spacing", text="Spacing Along Stroke") + col = layout.column() + col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier") + for modifier in linestyle.geometry_modifiers: + self.draw_geometry_modifier(context, modifier) - row = layout.row() - props = row.operator( - "wm.properties_context_change", - text="Go to Linestyle Textures Properties", - icon='TEXTURE', - ) - props.context = 'TEXTURE' - elif linestyle.panel == 'MISC': - pass +class VIEWLAYER_PT_freestyle_linestyle_texture(ViewLayerFreestyleLineStyle, Panel): + bl_label = "Freestyle Texture" + bl_options = {'DEFAULT_CLOSED'} + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False -# Material properties + view_layer = context.view_layer + lineset = view_layer.freestyle_settings.linesets.active + + layout.active = view_layer.use_freestyle + if lineset is None: + return + linestyle = lineset.linestyle + + if linestyle is None: + return + + row = layout.row(align=True) + row.alignment = 'LEFT' + row.label(text=lineset.name, icon='LINE_DATA') + row.label(text="", icon='SMALL_TRI_RIGHT_VEC') + row.label(text=linestyle.name) + + layout.prop(linestyle, "use_nodes") + layout.prop(linestyle, "texture_spacing", text="Spacing Along Stroke") + + row = layout.row() + props = row.operator( + "wm.properties_context_change", + text="Go to Linestyle Textures Properties", + icon='TEXTURE', + ) + props.context = 'TEXTURE' + + +# Material properties class MaterialFreestyleButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -825,8 +1243,11 @@ class MaterialFreestyleButtonsPanel: material = context.material with_freestyle = bpy.app.build_options.freestyle return ( - with_freestyle and material and scene and scene.render.use_freestyle and - (context.engine in cls.COMPAT_ENGINES) + with_freestyle + and material + and scene + and scene.render.use_freestyle + and (context.engine in cls.COMPAT_ENGINES) ) @@ -850,12 +1271,30 @@ classes = ( VIEWLAYER_UL_linesets, RENDER_MT_lineset_context_menu, VIEWLAYER_PT_freestyle, + VIEWLAYER_PT_freestyle_edge_detection_options, + VIEWLAYER_PT_freestyle_style_modules, VIEWLAYER_PT_freestyle_lineset, - VIEWLAYER_PT_freestyle_linestyle, + VIEWLAYER_PT_freestyle_lineset_visibilty, + VIEWLAYER_PT_freestyle_lineset_edgetype, + VIEWLAYER_PT_freestyle_lineset_facemarks, + VIEWLAYER_PT_freestyle_lineset_collection, + VIEWLAYER_PT_freestyle_linestyle_strokes, + VIEWLAYER_PT_freestyle_linestyle_strokes_chaining, + VIEWLAYER_PT_freestyle_linestyle_strokes_splitting, + VIEWLAYER_PT_freestyle_linestyle_strokes_splitting_pattern, + VIEWLAYER_PT_freestyle_linestyle_strokes_sorting, + VIEWLAYER_PT_freestyle_linestyle_strokes_selection, + VIEWLAYER_PT_freestyle_linestyle_strokes_dashedline, + VIEWLAYER_PT_freestyle_linestyle_color, + VIEWLAYER_PT_freestyle_linestyle_alpha, + VIEWLAYER_PT_freestyle_linestyle_thickness, + VIEWLAYER_PT_freestyle_linestyle_geometry, + VIEWLAYER_PT_freestyle_linestyle_texture, MATERIAL_PT_freestyle_line, ) if __name__ == "__main__": # only for live edit. from bpy.utils import register_class + for cls in classes: register_class(cls) diff --git a/source/blender/freestyle/intern/application/AppConfig.h b/source/blender/freestyle/intern/application/AppConfig.h index 61beff33876..adb6d906e68 100644 --- a/source/blender/freestyle/intern/application/AppConfig.h +++ b/source/blender/freestyle/intern/application/AppConfig.h @@ -115,10 +115,6 @@ static const string OPTIONS_FILE("options.xml"); static const string OPTIONS_CURRENT_DIRS_FILE("current_dirs.xml"); static const string OPTIONS_QGLVIEWER_FILE("qglviewer.xml"); -// Default options -static const real DEFAULT_SPHERE_RADIUS = 1.0; -static const real DEFAULT_DKR_EPSILON = 0.0; - } // namespace Config } /* namespace Freestyle */ diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index 7772a30c5f4..c74fd60fe35 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -65,9 +65,6 @@ using namespace Freestyle; extern "C" { -#define DEFAULT_SPHERE_RADIUS 1.0f -#define DEFAULT_DKR_EPSILON 0.0f - struct FreestyleGlobals g_freestyle; // Freestyle configuration @@ -433,14 +430,8 @@ static void prepare(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph) } // set parameters - if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) { - controller->setSphereRadius(config->sphere_radius); - controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon); - } - else { - controller->setSphereRadius(DEFAULT_SPHERE_RADIUS); - controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON); - } + controller->setSphereRadius(config->sphere_radius); + controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon); controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false); controller->setCreaseAngle(RAD2DEGF(config->crease_angle)); controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ? diff --git a/source/blender/makesdna/DNA_freestyle_types.h b/source/blender/makesdna/DNA_freestyle_types.h index 4d4fbaed29a..ab1e7aa903c 100644 --- a/source/blender/makesdna/DNA_freestyle_types.h +++ b/source/blender/makesdna/DNA_freestyle_types.h @@ -40,7 +40,7 @@ enum { FREESTYLE_RIDGES_AND_VALLEYS_FLAG = 1 << 1, FREESTYLE_MATERIAL_BOUNDARIES_FLAG = 1 << 2, FREESTYLE_FACE_SMOOTHNESS_FLAG = 1 << 3, - FREESTYLE_ADVANCED_OPTIONS_FLAG = 1 << 4, + /* FREESTYLE_ADVANCED_OPTIONS_FLAG = 1 << 4, */ /* UNUSED */ FREESTYLE_CULLING = 1 << 5, FREESTYLE_VIEW_MAP_CACHE = 1 << 6, FREESTYLE_AS_RENDER_PASS = 1 << 7, diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ce7c93a46b3..badaaa14aa4 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -4597,12 +4597,12 @@ void rna_def_freestyle_settings(BlenderRNA *brna) {FREESTYLE_CONTROL_SCRIPT_MODE, "SCRIPT", 0, - "Python Scripting Mode", + "Python Scripting", "Advanced mode for using style modules written in Python"}, {FREESTYLE_CONTROL_EDITOR_MODE, "EDITOR", 0, - "Parameter Editor Mode", + "Parameter Editor", "Basic mode for interactive style parameter editing"}, {0, NULL, 0, NULL, NULL}, }; @@ -4613,7 +4613,7 @@ void rna_def_freestyle_settings(BlenderRNA *brna) {FREESTYLE_QI_RANGE, "RANGE", 0, - "QI Range", + "Quantitative Invisibility", "Select feature edges within a range of quantitative invisibility (QI) values"}, {0, NULL, 0, NULL, NULL}, }; @@ -4930,14 +4930,6 @@ void rna_def_freestyle_settings(BlenderRNA *brna) prop, "Face Smoothness", "Take face smoothness into account in view map calculation"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); - prop = RNA_def_property(srna, "use_advanced_options", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_ADVANCED_OPTIONS_FLAG); - RNA_def_property_ui_text( - prop, - "Advanced Options", - "Enable advanced edge detection options (sphere radius and Kr derivative epsilon)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); - prop = RNA_def_property(srna, "use_view_map_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FREESTYLE_VIEW_MAP_CACHE); RNA_def_property_ui_text( @@ -4957,11 +4949,13 @@ void rna_def_freestyle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "sphere_radius", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "sphere_radius"); + RNA_def_property_float_default(prop, 1.0); RNA_def_property_range(prop, 0.0, 1000.0); RNA_def_property_ui_text(prop, "Sphere Radius", "Sphere radius for computing curvatures"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_freestyle_update"); prop = RNA_def_property(srna, "kr_derivative_epsilon", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_default(prop, 0.0); RNA_def_property_float_sdna(prop, NULL, "dkr_epsilon"); RNA_def_property_range(prop, -1000.0, 1000.0); RNA_def_property_ui_text( -- cgit v1.2.3 From 503d79cd779f103cbea74eca390419e8c6fd2aad Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 13 Sep 2021 15:06:02 +0200 Subject: Fix straightline gesture snapping not working for modal tools This was implemented in {rB14d56b4217f8} but was never working for tools/operators other than the sculpt line mask tool. To be precise, the preview actually snapped but the operations (e.g. mesh bisect, vertex weight gradient) still happened "unsnapped" in modal. For the sculpt line mask tool this wasnt a problem, because it only draws a preview while modal, the actual mask was only applied later. This solves part one of T91320 (snapping), sculpting also introduced flipping in {rB7ff6bfd1e0af} which does not make much sense for all tools, but in bisect this could actually be supported, will add that in a separate Diff. ref T91320 Maniphest Tasks: T91320 Differential Revision: https://developer.blender.org/D12470 --- source/blender/windowmanager/intern/wm_gesture_ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 1c736647084..0177e336121 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -993,6 +993,7 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev if (gesture->use_snap) { wm_gesture_straightline_do_angle_snap(rect); + gesture_straightline_apply(C, op); } wm_gesture_tag_redraw(win); -- cgit v1.2.3 From 1bcdd1c54e6e9034af2ad1d78b30b891ac42a655 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 13 Sep 2021 15:56:53 +0200 Subject: Fix T91320: Support flipping sides in mesh bisect Changing active side was introduced in {rB7ff6bfd1e0af} but was never working for tools/operators other than the sculpt line mask tool. While for most tools/operators this actually does not make sense, the bisect tool/operator can actually benefit from it. thx @campbellbarton for additional input! Maniphest Tasks: T91320 Differential Revision: https://developer.blender.org/D12473 --- source/blender/editors/mesh/editmesh_bisect.c | 17 ++++++++++++++++- source/blender/windowmanager/intern/wm_gesture_ops.c | 3 ++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 3c8afe8e7db..9f6968542a2 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -88,6 +88,7 @@ static void mesh_bisect_interactive_calc(bContext *C, int y_start = RNA_int_get(op->ptr, "ystart"); int x_end = RNA_int_get(op->ptr, "xend"); int y_end = RNA_int_get(op->ptr, "yend"); + const bool use_flip = RNA_boolean_get(op->ptr, "flip"); /* reference location (some point in front of the view) for finding a point on a plane */ const float *co_ref = rv3d->ofs; @@ -105,6 +106,9 @@ static void mesh_bisect_interactive_calc(bContext *C, /* cross both to get a normal */ cross_v3_v3v3(plane_no, co_a, co_b); normalize_v3(plane_no); /* not needed but nicer for user */ + if (use_flip) { + negate_v3(plane_no); + } /* point on plane, can use either start or endpoint */ ED_view3d_win_to_3d(v3d, region, co_ref, co_a_ss, plane_co); @@ -140,7 +144,18 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - int ret = WM_gesture_straightline_invoke(C, op, event); + /* Support flipping if side matters. */ + int ret; + const bool clear_inner = RNA_boolean_get(op->ptr, "clear_inner"); + const bool clear_outer = RNA_boolean_get(op->ptr, "clear_outer"); + const bool use_fill = RNA_boolean_get(op->ptr, "use_fill"); + if ((clear_inner != clear_outer) || use_fill) { + ret = WM_gesture_straightline_active_side_invoke(C, op, event); + } + else { + ret = WM_gesture_straightline_invoke(C, op, event); + } + if (ret & OPERATOR_RUNNING_MODAL) { View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 0177e336121..788e4214ac7 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -954,8 +954,9 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev break; } case GESTURE_MODAL_FLIP: { - /* Toggle snapping on/off. */ + /* Toggle flipping on/off. */ gesture->use_flip = !gesture->use_flip; + gesture_straightline_apply(C, op); break; } case GESTURE_MODAL_SELECT: { -- cgit v1.2.3 From a78410386b05f375b53ed867980234b61c2eba10 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 13 Sep 2021 16:49:32 +0200 Subject: Cleanup: fix typo in comment --- source/blender/editors/mesh/editmesh_bisect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 9f6968542a2..b511a1d2c14 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -120,7 +120,7 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) int valid_objects = 0; /* If the properties are set or there is no rv3d, - * skip model and exec immediately. */ + * skip modal and exec immediately. */ if ((CTX_wm_region_view3d(C) == NULL) || (RNA_struct_property_is_set(op->ptr, "plane_co") && RNA_struct_property_is_set(op->ptr, "plane_no"))) { return mesh_bisect_exec(C, op); -- cgit v1.2.3 From 4f73d43adc38a54c8b033ca3e91e9d61e927b665 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 13 Sep 2021 12:41:33 -0300 Subject: Fix T90736: Problem applying scale to curves Curve Points of handle of type `Auto` on curves not uniformly scaled cause the shape of the curve to be modified after applying the scale. So change these handles to `Aligned` in these cases. Reviewed By: campbellbarton Maniphest Tasks: T90736 Differential Revision: https://developer.blender.org/D12281 --- source/blender/blenkernel/intern/curve.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 397838e6904..f22c3b13efc 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -5269,6 +5269,8 @@ void BKE_curve_transform_ex(Curve *cu, BezTriple *bezt; int i; + const bool is_uniform_scaled = is_uniform_scaled_m4(mat); + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { if (nu->type == CU_BEZIER) { i = nu->pntsu; @@ -5279,6 +5281,11 @@ void BKE_curve_transform_ex(Curve *cu, if (do_props) { bezt->radius *= unit_scale; } + if (!is_uniform_scaled) { + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { + bezt->h1 = bezt->h2 = HD_ALIGN; + } + } } BKE_nurb_handles_calc(nu); } -- cgit v1.2.3 From 9fe6854a939169756895be680774ed1f66f206e7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 13 Sep 2021 17:47:16 +0200 Subject: Cleanup: Remove duplicate code Class SEQUENCER_PT_overlay as defined twice. --- release/scripts/startup/bl_ui/space_sequencer.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index be669b1fe86..258797c18da 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -191,16 +191,6 @@ class SEQUENCER_PT_overlay(Panel): pass -class SEQUENCER_PT_overlay(Panel): - bl_space_type = 'SEQUENCE_EDITOR' - bl_region_type = 'HEADER' - bl_label = "Overlays" - bl_ui_units_x = 7 - - def draw(self, _context): - pass - - class SEQUENCER_PT_preview_overlay(Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'HEADER' -- cgit v1.2.3 From 3ff60bcad8cccd6c2f82ea44527f4e6ef619f0be Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Mon, 13 Sep 2021 18:40:21 +0200 Subject: Asset Template: Extra UI options This allow users to show/hide: * Library name / refresh. * Assets names. * Filter. To set them in Python use: display_options={'NO_NAMES', 'NO_FILTER', 'NO_LIBRARY'} With contributions by Julian Eisel. Differential Revision: https://developer.blender.org/D12476 --- source/blender/editors/include/UI_interface.h | 12 ++++++++++ .../interface/interface_template_asset_view.cc | 26 +++++++++++++++----- .../editors/interface/interface_template_list.cc | 9 ++++++- .../blender/editors/interface/interface_widgets.c | 11 +++++++-- source/blender/makesrna/intern/rna_ui_api.c | 28 ++++++++++++++++++++++ 5 files changed, 77 insertions(+), 9 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7211cf9f893..916105b0f8e 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2209,6 +2209,11 @@ enum uiTemplateListFlags { UI_TEMPLATE_LIST_SORT_LOCK = (1 << 1), /* Don't allow resizing the list, i.e. don't add the grip button. */ UI_TEMPLATE_LIST_NO_GRIP = (1 << 2), + /** Do not show filtering options, not even the button to expand/collapse them. Also hides the + * grip button. */ + UI_TEMPLATE_LIST_NO_FILTER_OPTIONS = (1 << 3), + /** For #UILST_LAYOUT_BIG_PREVIEW_GRID, don't reserve space for the name label. */ + UI_TEMPLATE_LIST_NO_NAMES = (1 << 4), UI_TEMPLATE_LIST_FLAGS_LAST }; @@ -2289,6 +2294,12 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows); void uiTemplateFileSelectPath(uiLayout *layout, struct bContext *C, struct FileSelectParams *params); + +enum { + UI_TEMPLATE_ASSET_DRAW_NO_NAMES = (1 << 0), + UI_TEMPLATE_ASSET_DRAW_NO_FILTER = (1 << 1), + UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY = (1 << 2), +}; void uiTemplateAssetView(struct uiLayout *layout, struct bContext *C, const char *list_id, @@ -2299,6 +2310,7 @@ void uiTemplateAssetView(struct uiLayout *layout, struct PointerRNA *active_dataptr, const char *active_propname, const struct AssetFilterSettings *filter_settings, + const int display_flags, const char *activate_opname, struct PointerRNA *r_activate_op_properties, const char *drag_opname, diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 9b601727e29..f27b37a27de 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -46,6 +46,7 @@ struct AssetViewListData { AssetLibraryReference asset_library_ref; bScreen *screen; + bool show_names; }; static void asset_view_item_but_drag_set(uiBut *but, @@ -95,14 +96,15 @@ static void asset_view_draw_item(uiList *ui_list, uiLayoutSetContextPointer(layout, "asset_handle", itemptr); uiBlock *block = uiLayoutGetBlock(layout); + const bool show_names = list_data->show_names; /* TODO ED_fileselect_init_layout(). Share somehow? */ const float size_x = (96.0f / 20.0f) * UI_UNIT_X; - const float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + const float size_y = (96.0f / 20.0f) * UI_UNIT_Y - (show_names ? 0 : UI_UNIT_Y); uiBut *but = uiDefIconTextBut(block, UI_BTYPE_PREVIEW_TILE, 0, ED_asset_handle_get_preview_icon_id(asset_handle), - ED_asset_handle_get_name(asset_handle), + show_names ? ED_asset_handle_get_name(asset_handle) : "", 0, 0, size_x, @@ -202,6 +204,7 @@ void uiTemplateAssetView(uiLayout *layout, PointerRNA *active_dataptr, const char *active_propname, const AssetFilterSettings *filter_settings, + const int display_flags, const char *activate_opname, PointerRNA *r_activate_op_properties, const char *drag_opname, @@ -220,9 +223,11 @@ void uiTemplateAssetView(uiLayout *layout, RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); uiLayout *row = uiLayoutRow(col, true); - uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); - if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { - uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY) == 0) { + uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); + if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + } } ED_assetlist_storage_fetch(&asset_library_ref, C); @@ -236,6 +241,15 @@ void uiTemplateAssetView(uiLayout *layout, "AssetViewListData"); list_data->asset_library_ref = asset_library_ref; list_data->screen = CTX_wm_screen(C); + list_data->show_names = (display_flags & UI_TEMPLATE_ASSET_DRAW_NO_NAMES) == 0; + + uiTemplateListFlags template_list_flags = UI_TEMPLATE_LIST_NO_GRIP; + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_NAMES) != 0) { + template_list_flags |= UI_TEMPLATE_LIST_NO_NAMES; + } + if ((display_flags & UI_TEMPLATE_ASSET_DRAW_NO_FILTER) != 0) { + template_list_flags |= UI_TEMPLATE_LIST_NO_FILTER_OPTIONS; + } /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading * (of previews) of the items? */ @@ -252,7 +266,7 @@ void uiTemplateAssetView(uiLayout *layout, 0, UILST_LAYOUT_BIG_PREVIEW_GRID, 0, - UI_TEMPLATE_LIST_NO_GRIP, + template_list_flags, list_data); if (!list) { /* List creation failed. */ diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 0ab45ea0f81..8246759ad36 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -944,10 +944,16 @@ static void ui_template_list_layout_draw(bContext *C, /* For scrollbar. */ row = uiLayoutRow(glob, false); + const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0; + /* TODO ED_fileselect_init_layout(). Share somehow? */ float size_x = (96.0f / 20.0f) * UI_UNIT_X; float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + if (!show_names) { + size_y -= UI_UNIT_Y; + } + const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); @@ -1033,7 +1039,8 @@ static void ui_template_list_layout_draw(bContext *C, break; } - if (glob) { + const bool add_filters_but = (flags & UI_TEMPLATE_LIST_NO_FILTER_OPTIONS) == 0; + if (glob && add_filters_but) { const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; /* About #UI_BTYPE_GRIP drag-resize: diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index a2b86ccd947..0dc7c2d3f9a 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -5443,13 +5443,20 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle, rcti trect = *rect; const float text_size = UI_UNIT_Y; float font_dims[2] = {0.0f, 0.0f}; + const bool has_text = name && name[0]; - /* draw icon in rect above the space reserved for the label */ - rect->ymin += text_size; + if (has_text) { + /* draw icon in rect above the space reserved for the label */ + rect->ymin += text_size; + } GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(iconid, 1.0f, rect); GPU_blend(GPU_BLEND_NONE); + if (!has_text) { + return; + } + BLF_width_and_height( fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index e06cc39a88b..f96b3fc5eee 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -622,6 +622,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, PointerRNA *active_dataptr, const char *active_propname, int filter_id_types, + int display_flags, const char *activate_opname, PointerRNA *r_activate_op_properties, const char *drag_opname, @@ -630,6 +631,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, AssetFilterSettings filter_settings = { .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL, }; + uiTemplateAssetView(layout, C, list_id, @@ -640,6 +642,7 @@ static void rna_uiTemplateAssetView(uiLayout *layout, active_dataptr, active_propname, &filter_settings, + display_flags, activate_opname, r_activate_op_properties, drag_opname, @@ -878,6 +881,25 @@ void RNA_api_ui_layout(StructRNA *srna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem asset_view_template_options[] = { + {UI_TEMPLATE_ASSET_DRAW_NO_NAMES, + "NO_NAMES", + 0, + "", + "Do not display the name of each asset underneath preview images"}, + {UI_TEMPLATE_ASSET_DRAW_NO_FILTER, + "NO_FILTER", + 0, + "", + "Do not display buttons for filtering the available assets"}, + {UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY, + "NO_LIBRARY", + 0, + "", + "Do not display buttons to choose or refresh an asset library"}, + {0, NULL, 0, NULL, NULL}, + }; + static float node_socket_color_default[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* simple layout specifiers */ @@ -1839,6 +1861,12 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_property_enum_items(parm, DummyRNA_NULL_items); RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf"); RNA_def_property_flag(parm, PROP_ENUM_FLAG); + RNA_def_enum_flag(func, + "display_options", + asset_view_template_options, + 0, + "", + "Displaying options for the asset view"); RNA_def_string(func, "activate_operator", NULL, -- cgit v1.2.3 From 9b0b78d58fe55276b9b83cbb8a2c570cd215ed0a Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Mon, 13 Sep 2021 19:07:42 -0400 Subject: Update RNA to User Manual Mappings --- release/scripts/modules/rna_manual_reference.py | 125 +++++++++++++++++------- 1 file changed, 90 insertions(+), 35 deletions(-) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index f9756811bde..24b5bb6b685 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -47,6 +47,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_potential_min_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-wavecrest"), ("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"), ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"), + ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"), ("bpy.types.movietrackingsettings.refine_intrinsics_focal_length*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-focal-length"), @@ -56,6 +57,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"), + ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"), ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), @@ -65,6 +67,8 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.vector_scale_with_magnitude*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-scale-with-magnitude"), ("bpy.types.movietrackingstabilization.use_2d_stabilization*", "movie_clip/tracking/clip/sidebar/stabilization/panel.html#bpy-types-movietrackingstabilization-use-2d-stabilization"), ("bpy.types.spacespreadsheet.display_context_path_collapsed*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-display-context-path-collapsed"), + ("bpy.types.toolsettings.annotation_stroke_placement_view2d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view2d"), + ("bpy.types.toolsettings.annotation_stroke_placement_view3d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view3d"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), ("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"), @@ -85,11 +89,15 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), + ("bpy.types.sequencertoolsettings.snap_to_current_frame*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"), ("bpy.types.colormanageddisplaysettings.display_device*", "render/color_management.html#bpy-types-colormanageddisplaysettings-display-device"), ("bpy.types.colormanagedviewsettings.use_curve_mapping*", "render/color_management.html#bpy-types-colormanagedviewsettings-use-curve-mapping"), ("bpy.types.fluiddomainsettings.color_ramp_field_scale*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field-scale"), ("bpy.types.fluiddomainsettings.use_adaptive_timesteps*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-adaptive-timesteps"), ("bpy.types.fluiddomainsettings.use_dissolve_smoke_log*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke-log"), + ("bpy.types.gpencillayer.annotation_onion_before_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-color"), + ("bpy.types.gpencillayer.annotation_onion_before_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-range"), + ("bpy.types.gpencillayer.use_annotation_onion_skinning*", "interface/annotate_tool.html#bpy-types-gpencillayer-use-annotation-onion-skinning"), ("bpy.types.greasepencil.use_adaptive_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-adaptive-curve-resolution"), ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), ("bpy.types.toolsettings.use_gpencil_automerge_strokes*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-automerge-strokes"), @@ -102,10 +110,13 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.gridlines_lower_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-lower-bound"), ("bpy.types.fluiddomainsettings.gridlines_range_color*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-range-color"), ("bpy.types.fluiddomainsettings.gridlines_upper_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-upper-bound"), + ("bpy.types.gpencillayer.annotation_onion_after_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-color"), + ("bpy.types.gpencillayer.annotation_onion_after_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-range"), ("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"), ("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"), ("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"), - ("bpy.types.spacesequenceeditor.waveform_display_type*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-waveform-display-type"), + ("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"), + ("bpy.types.spacesequenceeditor.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-waveform-display-type"), ("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"), ("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"), ("bpy.types.brush.elastic_deform_volume_preservation*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-volume-preservation"), @@ -145,9 +156,11 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"), ("bpy.types.movietrackingsettings.use_tripod_solver*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-tripod-solver"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), + ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"), + ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"), ("bpy.types.spaceoutliner.use_filter_case_sensitive*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-case-sensitive"), ("bpy.types.spaceoutliner.use_filter_object_content*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-content"), - ("bpy.types.spacesequenceeditor.show_strip_duration*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-duration"), + ("bpy.types.spacesequenceeditor.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-duration"), ("bpy.types.toolsettings.use_proportional_connected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-connected"), ("bpy.types.toolsettings.use_proportional_projected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-projected"), ("bpy.types.view3doverlay.vertex_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-vertex-paint-mode-opacity"), @@ -156,6 +169,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.vertex_color_brightness_contrast*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-brightness-contrast"), ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"), ("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"), + ("bpy.types.cyclesobjectsettings.use_distance_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-distance-cull"), ("bpy.types.cyclesrendersettings.ao_bounces_render*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces-render"), ("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"), ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"), @@ -180,6 +194,7 @@ url_manual_mapping = ( ("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"), ("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"), ("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"), + ("bpy.types.spacesequenceeditor.show_strip_overlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-overlay"), ("bpy.types.toolsettings.proportional_edit_falloff*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-proportional-edit-falloff"), ("bpy.types.toolsettings.use_edge_path_live_unwrap*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-edge-path-live-unwrap"), ("bpy.types.toolsettings.use_gpencil_draw_additive*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-additive"), @@ -214,8 +229,8 @@ url_manual_mapping = ( ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), - ("bpy.types.spacesequenceeditor.show_strip_offset*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-offset"), - ("bpy.types.spacesequenceeditor.show_strip_source*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-source"), + ("bpy.types.spacesequenceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-offset"), + ("bpy.types.spacesequenceeditor.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-source"), ("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"), @@ -223,6 +238,7 @@ url_manual_mapping = ( ("bpy.types.viewlayer.use_pass_cryptomatte_object*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-object"), ("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"), ("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"), + ("bpy.types.cyclesobjectsettings.use_camera_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-camera-cull"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), ("bpy.types.editbone.bbone_handle_use_ease_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-start"), ("bpy.types.fluiddomainsettings.color_ramp_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field"), @@ -232,7 +248,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), ("bpy.types.movieclipuser.use_render_undistorted*", "editors/clip/display/clip_display.html#bpy-types-movieclipuser-use-render-undistorted"), ("bpy.types.movietrackingcamera.distortion_model*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-distortion-model"), - ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-percentage"), + ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-percentage"), ("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"), ("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"), ("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"), @@ -267,6 +283,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), ("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"), ("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"), + ("bpy.types.rendersettings.sequencer_gl_preview*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"), ("bpy.types.rendersettings.simplify_subdivision*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-subdivision"), ("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"), ("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"), @@ -274,7 +291,7 @@ url_manual_mapping = ( ("bpy.types.spacesequenceeditor.show_annotation*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-annotation"), ("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"), ("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"), - ("bpy.types.spacesequenceeditor.show_strip_name*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-name"), + ("bpy.types.spacesequenceeditor.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-name"), ("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"), ("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"), ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"), @@ -313,6 +330,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), ("bpy.types.nodesocketinterface*.default_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-default-value"), ("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"), + ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"), ("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"), ("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"), ("bpy.types.spacespreadsheet.object_eval_state*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-object-eval-state"), @@ -339,10 +357,10 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"), ("bpy.types.objectlineart.use_crease_override*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-use-crease-override"), ("bpy.types.rendersettings.preview_pixel_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-preview-pixel-size"), - ("bpy.types.rendersettings.use_crop_to_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-crop-to-border"), + ("bpy.types.rendersettings.use_crop_to_border*", "render/output/properties/format.html#bpy-types-rendersettings-use-crop-to-border"), ("bpy.types.rendersettings.use_file_extension*", "render/output/properties/output.html#bpy-types-rendersettings-use-file-extension"), ("bpy.types.sculpt.constant_detail_resolution*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-constant-detail-resolution"), - ("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/annotation.html#bpy-types-spaceclipeditor-annotation-source"), + ("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/view.html#bpy-types-spaceclipeditor-annotation-source"), ("bpy.types.spaceclipeditor.show_blue_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-blue-channel"), ("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"), ("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"), @@ -388,7 +406,7 @@ url_manual_mapping = ( ("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"), ("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"), ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"), - ("bpy.types.spacesequenceeditor.show_fcurves*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-fcurves"), + ("bpy.types.spacesequenceeditor.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-fcurves"), ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"), ("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"), ("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"), @@ -396,6 +414,7 @@ url_manual_mapping = ( ("bpy.ops.clip.stabilize_2d_rotation_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-rotation-select"), ("bpy.ops.constraint.disable_keep_transform*", "animation/constraints/interface/common.html#bpy-ops-constraint-disable-keep-transform"), ("bpy.ops.gpencil.stroke_reset_vertex_color*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-stroke-reset-vertex-color"), + ("bpy.ops.object.modifier_apply_as_shapekey*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-apply-as-shapekey"), ("bpy.ops.object.vertex_group_normalize_all*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize-all"), ("bpy.ops.outliner.collection_color_tag_set*", "editors/outliner/editing.html#bpy-ops-outliner-collection-color-tag-set"), ("bpy.ops.outliner.collection_enable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-enable-render"), @@ -417,6 +436,7 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.flow_behavior*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-behavior"), ("bpy.types.fluidflowsettings.noise_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-noise-texture"), ("bpy.types.geometrynodeattributevectormath*", "modeling/geometry_nodes/attribute/attribute_vector_math.html#bpy-types-geometrynodeattributevectormath"), + ("bpy.types.gpencillayer.annotation_opacity*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-opacity"), ("bpy.types.gpencillayer.use_onion_skinning*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-onion-skinning"), ("bpy.types.gpencilsculptguide.use_snapping*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-use-snapping"), ("bpy.types.gpencilsculptsettings.lock_axis*", "grease_pencil/modes/draw/drawing_planes.html#bpy-types-gpencilsculptsettings-lock-axis"), @@ -443,6 +463,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.gpencil_selectmode*", "grease_pencil/selecting.html#bpy-types-toolsettings-gpencil-selectmode"), ("bpy.types.toolsettings.use_auto_normalize*", "sculpt_paint/weight_paint/tool_settings/options.html#bpy-types-toolsettings-use-auto-normalize"), ("bpy.types.toolsettings.use_mesh_automerge*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge"), + ("bpy.types.toolsettings.use_snap_sequencer*", "video_editing/sequencer/editing.html#bpy-types-toolsettings-use-snap-sequencer"), ("bpy.types.toolsettings.use_snap_translate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-translate"), ("bpy.types.toolsettings.use_uv_select_sync*", "editors/uv/selecting.html#bpy-types-toolsettings-use-uv-select-sync"), ("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"), @@ -469,7 +490,6 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"), ("bpy.types.fluiddomainsettings.guide_beta*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-beta"), ("bpy.types.fluiddomainsettings.mesh_scale*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-scale"), - ("bpy.types.fluiddomainsettings.noise_type*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-noise-type"), ("bpy.types.fluiddomainsettings.slice_axis*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-slice-axis"), ("bpy.types.fluiddomainsettings.time_scale*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-time-scale"), ("bpy.types.fluidflowsettings.texture_size*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-texture-size"), @@ -534,7 +554,7 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.smoke_color*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-smoke-color"), ("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"), ("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"), - ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), + ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), ("bpy.types.geometrynodeattributecurvemap*", "modeling/geometry_nodes/attribute/attribute_curve_map.html#bpy-types-geometrynodeattributecurvemap"), ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"), ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/attribute_transfer.html#bpy-types-geometrynodeattributetransfer"), @@ -543,13 +563,15 @@ url_manual_mapping = ( ("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"), ("bpy.types.movietrackingcamera.principal*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-principal"), ("bpy.types.object.use_camera_lock_parent*", "scene_layout/object/properties/relations.html#bpy-types-object-use-camera-lock-parent"), - ("bpy.types.rendersettings.pixel_aspect_x*", "render/output/properties/dimensions.html#bpy-types-rendersettings-pixel-aspect-x"), - ("bpy.types.rendersettings.pixel_aspect_y*", "render/output/properties/dimensions.html#bpy-types-rendersettings-pixel-aspect-y"), + ("bpy.types.object.visible_volume_scatter*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-volume-scatter"), + ("bpy.types.rendersettings.pixel_aspect_x*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-x"), + ("bpy.types.rendersettings.pixel_aspect_y*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-y"), ("bpy.types.rigidbodyconstraint.use_limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-limit"), ("bpy.types.sceneeevee.taa_render_samples*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-taa-render-samples"), ("bpy.types.spaceoutliner.use_sync_select*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-sync-select"), ("bpy.types.spaceproperties.outliner_sync*", "editors/properties_editor.html#bpy-types-spaceproperties-outliner-sync"), ("bpy.types.spaceproperties.search_filter*", "editors/properties_editor.html#bpy-types-spaceproperties-search-filter"), + ("bpy.types.spacesequenceeditor.view_type*", "editors/video_sequencer/introduction.html#bpy-types-spacesequenceeditor-view-type"), ("bpy.types.spacetexteditor.margin_column*", "editors/text_editor.html#bpy-types-spacetexteditor-margin-column"), ("bpy.types.spacetexteditor.use_find_wrap*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-wrap"), ("bpy.types.spaceuveditor.pixel_snap_mode*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-pixel-snap-mode"), @@ -586,9 +608,10 @@ url_manual_mapping = ( ("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"), ("bpy.types.materialgpencilstyle.pattern*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pattern"), ("bpy.types.materialgpencilstyle.texture*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-texture"), + ("bpy.types.modifier.use_apply_on_spline*", "modeling/modifiers/introduction.html#bpy-types-modifier-use-apply-on-spline"), ("bpy.types.object.use_empty_image_alpha*", "modeling/empties.html#bpy-types-object-use-empty-image-alpha"), - ("bpy.types.rendersettings.frame_map_new*", "render/output/properties/dimensions.html#bpy-types-rendersettings-frame-map-new"), - ("bpy.types.rendersettings.frame_map_old*", "render/output/properties/dimensions.html#bpy-types-rendersettings-frame-map-old"), + ("bpy.types.rendersettings.frame_map_new*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-new"), + ("bpy.types.rendersettings.frame_map_old*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-old"), ("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"), ("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"), ("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"), @@ -613,6 +636,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.primitive_ico_sphere_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-ico-sphere-add"), ("bpy.ops.object.gpencil_modifier_apply*", "grease_pencil/modifiers/introduction.html#bpy-ops-object-gpencil-modifier-apply"), ("bpy.ops.object.material_slot_deselect*", "render/materials/assignment.html#bpy-ops-object-material-slot-deselect"), + ("bpy.ops.object.modifier_move_to_index*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-move-to-index"), ("bpy.ops.object.multires_external_save*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-external-save"), ("bpy.ops.object.vertex_group_normalize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize"), ("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"), @@ -645,11 +669,12 @@ url_manual_mapping = ( ("bpy.types.linestyle*modifier_material*", "render/freestyle/parameter_editor/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), ("bpy.types.movietrackingcamera.brown_k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-k"), ("bpy.types.movietrackingcamera.brown_p*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-p"), + ("bpy.types.object.visible_transmission*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-transmission"), ("bpy.types.particlesettingstextureslot*", "physics/particles/texture_influence.html#bpy-types-particlesettingstextureslot"), ("bpy.types.posebone.ik_rotation_weight*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-ik-rotation-weight"), ("bpy.types.regionview3d.show_sync_view*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-show-sync-view"), - ("bpy.types.rendersettings.resolution_x*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-x"), - ("bpy.types.rendersettings.resolution_y*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-y"), + ("bpy.types.rendersettings.resolution_x*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-x"), + ("bpy.types.rendersettings.resolution_y*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-y"), ("bpy.types.rendersettings.threads_mode*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-threads-mode"), ("bpy.types.rigidbodyconstraint.enabled*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-enabled"), ("bpy.types.rigidbodyconstraint.object1*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-object1"), @@ -700,7 +725,7 @@ url_manual_mapping = ( ("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"), ("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"), ("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"), - ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierfunctiongenerator"), + ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"), ("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"), ("bpy.types.geometrynodecurveendpoints*", "modeling/geometry_nodes/curve/curve_endpoints.html#bpy-types-geometrynodecurveendpoints"), @@ -710,6 +735,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodepointstovolume*", "modeling/geometry_nodes/volume/points_to_volume.html#bpy-types-geometrynodepointstovolume"), ("bpy.types.geometrynodepointtranslate*", "modeling/geometry_nodes/point/point_translate.html#bpy-types-geometrynodepointtranslate"), ("bpy.types.greasepencil.use_multiedit*", "grease_pencil/multiframe.html#bpy-types-greasepencil-use-multiedit"), + ("bpy.types.keyframe.handle_right_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right-type"), ("bpy.types.materialgpencilstyle.color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-color"), ("bpy.types.movietrackingcamera.nuke_k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-nuke-k"), ("bpy.types.movietrackingstabilization*", "movie_clip/tracking/clip/sidebar/stabilization/index.html#bpy-types-movietrackingstabilization"), @@ -728,6 +754,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_snap_self*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-self"), ("bpy.types.viewlayer.active_aov_index*", "render/layers/passes.html#bpy-types-viewlayer-active-aov-index"), ("bpy.ops.anim.channels_enable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-enable-toggle"), + ("bpy.ops.constraint.copy_to_selected*", "animation/constraints/interface/header.html#bpy-ops-constraint-copy-to-selected"), ("bpy.ops.gpencil.bake_mesh_animation*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-bake-mesh-animation"), ("bpy.ops.gpencil.select_vertex_color*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-vertex-color"), ("bpy.ops.gpencil.set_active_material*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-set-active-material"), @@ -778,13 +805,15 @@ url_manual_mapping = ( ("bpy.types.geometrynodepointinstance*", "modeling/geometry_nodes/point/point_instance.html#bpy-types-geometrynodepointinstance"), ("bpy.types.geometrynodepointseparate*", "modeling/geometry_nodes/point/point_separate.html#bpy-types-geometrynodepointseparate"), ("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"), + ("bpy.types.keyframe.handle_left_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left-type"), ("bpy.types.light.use_custom_distance*", "render/eevee/lighting.html#bpy-types-light-use-custom-distance"), ("bpy.types.materialgpencilstyle.flip*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-flip"), ("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mode"), + ("bpy.types.modifier.show_in_editmode*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-in-editmode"), ("bpy.types.object.empty_display_size*", "modeling/empties.html#bpy-types-object-empty-display-size"), ("bpy.types.object.empty_display_type*", "modeling/empties.html#bpy-types-object-empty-display-type"), ("bpy.types.regionview3d.use_box_clip*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-use-box-clip"), - ("bpy.types.rendersettings.use_border*", "render/output/properties/dimensions.html#bpy-types-rendersettings-use-border"), + ("bpy.types.rendersettings.use_border*", "render/output/properties/format.html#bpy-types-rendersettings-use-border"), ("bpy.types.rigidbodyconstraint.limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-limit"), ("bpy.types.rigidbodyobject.kinematic*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-kinematic"), ("bpy.types.scene.audio_doppler_speed*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-speed"), @@ -848,7 +877,6 @@ url_manual_mapping = ( ("bpy.types.copytransformsconstraint*", "animation/constraints/transform/copy_transforms.html#bpy-types-copytransformsconstraint"), ("bpy.types.correctivesmoothmodifier*", "modeling/modifiers/deform/corrective_smooth.html#bpy-types-correctivesmoothmodifier"), ("bpy.types.curve.bevel_factor_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-start"), - ("bpy.types.cyclesvisibilitysettings*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesvisibilitysettings"), ("bpy.types.fluiddomainsettings.beta*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-beta"), ("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"), ("bpy.types.functionnodefloatcompare*", "modeling/geometry_nodes/utilities/float_compare.html#bpy-types-functionnodefloatcompare"), @@ -865,6 +893,7 @@ url_manual_mapping = ( ("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"), ("bpy.types.movieclip.display_aspect*", "editors/clip/display/clip_display.html#bpy-types-movieclip-display-aspect"), ("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"), + ("bpy.types.object.is_shadow_catcher*", "render/cycles/object_settings/object_data.html#bpy-types-object-is-shadow-catcher"), ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"), ("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"), ("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"), @@ -955,7 +984,7 @@ url_manual_mapping = ( ("bpy.types.limitrotationconstraint*", "animation/constraints/transform/limit_rotation.html#bpy-types-limitrotationconstraint"), ("bpy.types.multiplygpencilmodifier*", "grease_pencil/modifiers/generate/multiple_strokes.html#bpy-types-multiplygpencilmodifier"), ("bpy.types.rendersettings.filepath*", "render/output/properties/output.html#bpy-types-rendersettings-filepath"), - ("bpy.types.rendersettings.fps_base*", "render/output/properties/dimensions.html#bpy-types-rendersettings-fps-base"), + ("bpy.types.rendersettings.fps_base*", "render/output/properties/format.html#bpy-types-rendersettings-fps-base"), ("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"), ("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"), ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"), @@ -965,13 +994,13 @@ url_manual_mapping = ( ("bpy.types.shadernodevolumescatter*", "render/shader_nodes/shader/volume_scatter.html#bpy-types-shadernodevolumescatter"), ("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"), ("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"), - ("bpy.types.toolsettings.annotation*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation"), ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"), ("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"), ("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"), ("bpy.ops.armature.select_linked()*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"), ("bpy.ops.clip.stabilize_2d_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-select"), + ("bpy.ops.constraint.move_to_index*", "animation/constraints/interface/header.html#bpy-ops-constraint-move-to-index"), ("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"), ("bpy.ops.gpencil.stroke_subdivide*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-subdivide"), ("bpy.ops.gpencil.vertex_color_hsv*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-hsv"), @@ -994,7 +1023,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_hide*", "editors/outliner/editing.html#bpy-ops-outliner-collection-hide"), ("bpy.ops.outliner.collection_show*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show"), ("bpy.ops.paint.mask_lasso_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-lasso-gesture"), - ("bpy.ops.rigidbody.mass_calculate*", "physics/rigid_body/editing.html#bpy-ops-rigidbody-mass-calculate"), + ("bpy.ops.rigidbody.mass_calculate*", "scene_layout/object/editing/rigid_body.html#bpy-ops-rigidbody-mass-calculate"), ("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"), ("bpy.ops.sculpt.detail_flood_fill*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-detail-flood-fill"), ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"), @@ -1045,9 +1074,12 @@ url_manual_mapping = ( ("bpy.types.geometrynodepointscale*", "modeling/geometry_nodes/point/point_scale.html#bpy-types-geometrynodepointscale"), ("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"), ("bpy.types.imagesequence.use_flip*", "video_editing/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"), + ("bpy.types.keyframe.interpolation*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-interpolation"), ("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"), ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"), ("bpy.types.mesh.auto_smooth_angle*", "modeling/meshes/structure.html#bpy-types-mesh-auto-smooth-angle"), + ("bpy.types.modifier.show_viewport*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-viewport"), + ("bpy.types.object.visible_diffuse*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-diffuse"), ("bpy.types.objectsolverconstraint*", "animation/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"), ("bpy.types.opacitygpencilmodifier*", "grease_pencil/modifiers/color/opacity.html#bpy-types-opacitygpencilmodifier"), ("bpy.types.particlesystemmodifier*", "physics/particles/index.html#bpy-types-particlesystemmodifier"), @@ -1143,6 +1175,7 @@ url_manual_mapping = ( ("bpy.types.dopesheet.filter_text*", "editors/graph_editor/channels.html#bpy-types-dopesheet-filter-text"), ("bpy.types.editbone.bbone_easein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-easein"), ("bpy.types.editbone.bbone_rollin*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollin"), + ("bpy.types.fcurve.auto_smoothing*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-auto-smoothing"), ("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"), ("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"), ("bpy.types.geometrynodecurveline*", "modeling/geometry_nodes/curve_primitives/line.html#bpy-types-geometrynodecurveline"), @@ -1151,12 +1184,17 @@ url_manual_mapping = ( ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/edge_split.html#bpy-types-geometrynodeedgesplit"), ("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"), ("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"), + ("bpy.types.keyframe.handle_right*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right"), ("bpy.types.light.cutoff_distance*", "render/eevee/lighting.html#bpy-types-light-cutoff-distance"), ("bpy.types.lockedtrackconstraint*", "animation/constraints/tracking/locked_track.html#bpy-types-lockedtrackconstraint"), ("bpy.types.material.blend_method*", "render/eevee/materials/settings.html#bpy-types-material-blend-method"), ("bpy.types.mirrorgpencilmodifier*", "grease_pencil/modifiers/generate/mirror.html#bpy-types-mirrorgpencilmodifier"), + ("bpy.types.modifier.show_on_cage*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-on-cage"), ("bpy.types.movietrackingcamera.k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-k"), ("bpy.types.node.use_custom_color*", "interface/controls/nodes/sidebar.html#bpy-types-node-use-custom-color"), + ("bpy.types.object.visible_camera*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-camera"), + ("bpy.types.object.visible_glossy*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-glossy"), + ("bpy.types.object.visible_shadow*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-shadow"), ("bpy.types.offsetgpencilmodifier*", "grease_pencil/modifiers/deform/offset.html#bpy-types-offsetgpencilmodifier"), ("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"), ("bpy.types.rendersettings.tile_x*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-x"), @@ -1192,6 +1230,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.vert_connect_path*", "modeling/meshes/editing/vertex/connect_vertex_path.html#bpy-ops-mesh-vert-connect-path"), ("bpy.ops.nla.action_sync_length*", "editors/nla/editing.html#bpy-ops-nla-action-sync-length"), ("bpy.ops.object.make_links_data*", "scene_layout/object/editing/link_transfer/link_data.html#bpy-ops-object-make-links-data"), + ("bpy.ops.object.modifier_remove*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-remove"), ("bpy.ops.object.paths_calculate*", "animation/motion_paths.html#bpy-ops-object-paths-calculate"), ("bpy.ops.object.transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-transform-apply"), ("bpy.ops.outliner.lib_operation*", "files/linked_libraries/link_append.html#bpy-ops-outliner-lib-operation"), @@ -1232,11 +1271,13 @@ url_manual_mapping = ( ("bpy.types.geometrynodemeshline*", "modeling/geometry_nodes/mesh_primitives/line.html#bpy-types-geometrynodemeshline"), ("bpy.types.gpencillayer.opacity*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-opacity"), ("bpy.types.image.display_aspect*", "editors/image/sidebar.html#bpy-types-image-display-aspect"), + ("bpy.types.keyframe.handle_left*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left"), ("bpy.types.keyingsetsall.active*", "editors/timeline.html#bpy-types-keyingsetsall-active"), ("bpy.types.limitscaleconstraint*", "animation/constraints/transform/limit_scale.html#bpy-types-limitscaleconstraint"), ("bpy.types.materialgpencilstyle*", "grease_pencil/materials/index.html#bpy-types-materialgpencilstyle"), ("bpy.types.mesh.use_auto_smooth*", "modeling/meshes/structure.html#bpy-types-mesh-use-auto-smooth"), ("bpy.types.meshtovolumemodifier*", "modeling/modifiers/generate/mesh_to_volume.html#bpy-types-meshtovolumemodifier"), + ("bpy.types.modifier.show_render*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-render"), ("bpy.types.noisegpencilmodifier*", "grease_pencil/modifiers/deform/noise.html#bpy-types-noisegpencilmodifier"), ("bpy.types.object.hide_viewport*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-viewport"), ("bpy.types.posebone.rigify_type*", "addons/rigging/rigify/rig_types/index.html#bpy-types-posebone-rigify-type"), @@ -1291,6 +1332,7 @@ url_manual_mapping = ( ("bpy.ops.node.tree_socket_move*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-move"), ("bpy.ops.object.duplicate_move*", "scene_layout/object/editing/duplicate.html#bpy-ops-object-duplicate-move"), ("bpy.ops.object.hook_add_selob*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook-add-selob"), + ("bpy.ops.object.modifier_apply*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-apply"), ("bpy.ops.object.select_by_type*", "scene_layout/object/selecting.html#bpy-ops-object-select-by-type"), ("bpy.ops.object.select_grouped*", "scene_layout/object/selecting.html#bpy-ops-object-select-grouped"), ("bpy.ops.object.select_pattern*", "scene_layout/object/selecting.html#bpy-ops-object-select-pattern"), @@ -1402,6 +1444,7 @@ url_manual_mapping = ( ("bpy.ops.node.read_viewlayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-viewlayers"), ("bpy.ops.node.tree_socket_add*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-add"), ("bpy.ops.object.data_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data.html#bpy-ops-object-data-transfer"), + ("bpy.ops.object.modifier_copy*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy"), ("bpy.ops.object.select_camera*", "scene_layout/object/selecting.html#bpy-ops-object-select-camera"), ("bpy.ops.object.select_linked*", "scene_layout/object/selecting.html#bpy-ops-object-select-linked"), ("bpy.ops.object.select_mirror*", "scene_layout/object/selecting.html#bpy-ops-object-select-mirror"), @@ -1436,13 +1479,15 @@ url_manual_mapping = ( ("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"), ("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"), ("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"), + ("bpy.types.constraint.enabled*", "animation/constraints/interface/header.html#bpy-types-constraint-enabled"), ("bpy.types.curve.bevel_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-object"), ("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"), ("bpy.types.curve.resolution_v*", "modeling/surfaces/properties/shape.html#bpy-types-curve-resolution-v"), ("bpy.types.curve.taper_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-object"), ("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"), ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), - ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifiergenerator"), + ("bpy.types.fcurve.array_index*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-array-index"), + ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), ("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"), @@ -1459,7 +1504,7 @@ url_manual_mapping = ( ("bpy.types.object.hide_select*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-select"), ("bpy.types.object.parent_type*", "scene_layout/object/properties/relations.html#bpy-types-object-parent-type"), ("bpy.types.object.show_bounds*", "scene_layout/object/properties/display.html#bpy-types-object-show-bounds"), - ("bpy.types.rendersettings.fps*", "render/output/properties/dimensions.html#bpy-types-rendersettings-fps"), + ("bpy.types.rendersettings.fps*", "render/output/properties/format.html#bpy-types-rendersettings-fps"), ("bpy.types.scene.audio_volume*", "scene_layout/scene/properties.html#bpy-types-scene-audio-volume"), ("bpy.types.sculpt.detail_size*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-size"), ("bpy.types.shadernodebsdfhair*", "render/shader_nodes/shader/hair.html#bpy-types-shadernodebsdfhair"), @@ -1558,8 +1603,9 @@ url_manual_mapping = ( ("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"), ("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"), ("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"), + ("bpy.types.fcurve.color_mode*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-color-mode"), ("bpy.types.fluidflowsettings*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings"), - ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierenvelope"), + ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"), ("bpy.types.freestylesettings*", "render/freestyle/view_layer.html#bpy-types-freestylesettings"), ("bpy.types.geometrynodegroup*", "modeling/geometry_nodes/group.html#bpy-types-geometrynodegroup"), ("bpy.types.gpencillayer.hide*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-hide"), @@ -1573,12 +1619,13 @@ url_manual_mapping = ( ("bpy.types.meshcachemodifier*", "modeling/modifiers/modify/mesh_cache.html#bpy-types-meshcachemodifier"), ("bpy.types.movieclipsequence*", "video_editing/sequencer/strips/clip.html#bpy-types-movieclipsequence"), ("bpy.types.object.dimensions*", "scene_layout/object/properties/transforms.html#bpy-types-object-dimensions"), + ("bpy.types.object.is_holdout*", "scene_layout/object/properties/visibility.html#bpy-types-object-is-holdout"), ("bpy.types.object.pass_index*", "scene_layout/object/properties/relations.html#bpy-types-object-pass-index"), ("bpy.types.object.track_axis*", "scene_layout/object/properties/relations.html#bpy-types-object-track-axis"), ("bpy.types.pose.use_mirror_x*", "animation/armatures/posing/tool_settings.html#bpy-types-pose-use-mirror-x"), ("bpy.types.preferencessystem*", "editors/preferences/system.html#bpy-types-preferencessystem"), ("bpy.types.scene.active_clip*", "scene_layout/scene/properties.html#bpy-types-scene-active-clip"), - ("bpy.types.scene.frame_start*", "render/output/properties/dimensions.html#bpy-types-scene-frame-start"), + ("bpy.types.scene.frame_start*", "render/output/properties/frame_range.html#bpy-types-scene-frame-start"), ("bpy.types.sceneeevee.shadow*", "render/eevee/render_settings/shadows.html#bpy-types-sceneeevee-shadow"), ("bpy.types.screen.use_follow*", "editors/timeline.html#bpy-types-screen-use-follow"), ("bpy.types.sequencetransform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform"), @@ -1661,7 +1708,8 @@ url_manual_mapping = ( ("bpy.types.displaysafeareas*", "render/cameras.html#bpy-types-displaysafeareas"), ("bpy.types.editbone.bbone_x*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-x"), ("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"), - ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierstepped"), + ("bpy.types.fcurve.data_path*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-data-path"), + ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"), ("bpy.types.freestylelineset*", "render/freestyle/parameter_editor/line_set.html#bpy-types-freestylelineset"), ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), ("bpy.types.mesh.*customdata*", "modeling/meshes/properties/custom_data.html#bpy-types-mesh-customdata"), @@ -1675,7 +1723,7 @@ url_manual_mapping = ( ("bpy.types.pose.use_auto_ik*", "animation/armatures/posing/tool_settings.html#bpy-types-pose-use-auto-ik"), ("bpy.types.preferencesinput*", "editors/preferences/input.html#bpy-types-preferencesinput"), ("bpy.types.rigifyparameters*", "addons/rigging/rigify/rig_types/index.html#bpy-types-rigifyparameters"), - ("bpy.types.scene.frame_step*", "render/output/properties/dimensions.html#bpy-types-scene-frame-step"), + ("bpy.types.scene.frame_step*", "render/output/properties/frame_range.html#bpy-types-scene-frame-step"), ("bpy.types.sceneeevee.bloom*", "render/eevee/render_settings/bloom.html#bpy-types-sceneeevee-bloom"), ("bpy.types.sculpt.show_mask*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-sculpt-show-mask"), ("bpy.types.sequence.channel*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-channel"), @@ -1708,6 +1756,7 @@ url_manual_mapping = ( ("bpy.ops.clip.clean_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clean-tracks"), ("bpy.ops.clip.delete_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-delete-track"), ("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"), + ("bpy.ops.constraint.delete*", "animation/constraints/interface/header.html#bpy-ops-constraint-delete"), ("bpy.ops.curve.smooth_tilt*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-tilt"), ("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"), ("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"), @@ -1751,15 +1800,16 @@ url_manual_mapping = ( ("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"), ("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"), ("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"), + ("bpy.types.constraint.name*", "animation/constraints/interface/header.html#bpy-types-constraint-name"), ("bpy.types.curve.eval_time*", "modeling/curves/properties/path_animation.html#bpy-types-curve-eval-time"), ("bpy.types.curve.fill_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-fill-mode"), ("bpy.types.editbone.layers*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-layers"), ("bpy.types.editbone.parent*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-parent"), ("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"), - ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fcurvemodifiers"), + ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"), ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), - ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifiercycles"), - ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierlimits"), + ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"), + ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"), ("bpy.types.imagepaint.mode*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-mode"), ("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"), ("bpy.types.musgravetexture*", "render/materials/legacy_textures/types/musgrave.html#bpy-types-musgravetexture"), @@ -1770,7 +1820,7 @@ url_manual_mapping = ( ("bpy.types.preferencesedit*", "editors/preferences/editing.html#bpy-types-preferencesedit"), ("bpy.types.preferencesview*", "editors/preferences/interface.html#bpy-types-preferencesview"), ("bpy.types.rigidbodyobject*", "physics/rigid_body/index.html#bpy-types-rigidbodyobject"), - ("bpy.types.scene.frame_end*", "render/output/properties/dimensions.html#bpy-types-scene-frame-end"), + ("bpy.types.scene.frame_end*", "render/output/properties/frame_range.html#bpy-types-scene-frame-end"), ("bpy.types.sceneeevee.gtao*", "render/eevee/render_settings/ambient_occlusion.html#bpy-types-sceneeevee-gtao"), ("bpy.types.screen.use_play*", "editors/timeline.html#bpy-types-screen-use-play"), ("bpy.types.shadernodebevel*", "render/shader_nodes/input/bevel.html#bpy-types-shadernodebevel"), @@ -1786,6 +1836,7 @@ url_manual_mapping = ( ("bpy.types.texturenodemath*", "editors/texture_node/types/converter/math.html#bpy-types-texturenodemath"), ("bpy.types.volume.filepath*", "modeling/volumes/properties.html#bpy-types-volume-filepath"), ("bpy.ops.clip.join_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-join-tracks"), + ("bpy.ops.constraint.apply*", "animation/constraints/interface/header.html#bpy-ops-constraint-apply"), ("bpy.ops.curve.select_row*", "modeling/surfaces/selecting.html#bpy-ops-curve-select-row"), ("bpy.ops.curve.tilt_clear*", "modeling/curves/editing/control_points.html#bpy-ops-curve-tilt-clear"), ("bpy.ops.curve.vertex_add*", "modeling/curves/editing/other.html#bpy-ops-curve-vertex-add"), @@ -1829,7 +1880,8 @@ url_manual_mapping = ( ("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"), ("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"), ("bpy.types.ffmpegsettings*", "render/output/properties/output.html#bpy-types-ffmpegsettings"), - ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifiernoise"), + ("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiernoise"), + ("bpy.types.keyframe.co_ui*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-co-ui"), ("bpy.types.mask.frame_end*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-end"), ("bpy.types.material.paint*", "sculpt_paint/texture_paint/index.html#bpy-types-material-paint"), ("bpy.types.mirrormodifier*", "modeling/modifiers/generate/mirror.html#bpy-types-mirrormodifier"), @@ -1861,6 +1913,7 @@ url_manual_mapping = ( ("bpy.ops.clip.select_all*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-all"), ("bpy.ops.clip.select_box*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-box"), ("bpy.ops.clip.set_origin*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-origin"), + ("bpy.ops.constraint.copy*", "animation/constraints/interface/header.html#bpy-ops-constraint-copy"), ("bpy.ops.curve.subdivide*", "modeling/curves/editing/segments.html#bpy-ops-curve-subdivide"), ("bpy.ops.ed.undo_history*", "interface/undo_redo.html#bpy-ops-ed-undo-history"), ("bpy.ops.file.unpack_all*", "files/blend/packed_data.html#bpy-ops-file-unpack-all"), @@ -1924,6 +1977,7 @@ url_manual_mapping = ( ("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"), ("bpy.types.imagesequence*", "video_editing/sequencer/strips/image.html#bpy-types-imagesequence"), ("bpy.types.marbletexture*", "render/materials/legacy_textures/types/marble.html#bpy-types-marbletexture"), + ("bpy.types.modifier.name*", "modeling/modifiers/introduction.html#bpy-types-modifier-name"), ("bpy.types.modifier.show*", "modeling/modifiers/introduction.html#bpy-types-modifier-show"), ("bpy.types.moviesequence*", "video_editing/sequencer/strips/movie.html#bpy-types-moviesequence"), ("bpy.types.movietracking*", "movie_clip/tracking/index.html#bpy-types-movietracking"), @@ -2106,6 +2160,7 @@ url_manual_mapping = ( ("bpy.types.compositor*", "compositing/index.html#bpy-types-compositor"), ("bpy.types.constraint*", "animation/constraints/index.html#bpy-types-constraint"), ("bpy.types.imagepaint*", "sculpt_paint/texture_paint/index.html#bpy-types-imagepaint"), + ("bpy.types.keymapitem*", "editors/preferences/keymap.html#bpy-types-keymapitem"), ("bpy.types.lightprobe*", "render/eevee/light_probes/index.html#bpy-types-lightprobe"), ("bpy.types.maskparent*", "movie_clip/masking/sidebar.html#bpy-types-maskparent"), ("bpy.types.maskspline*", "movie_clip/masking/sidebar.html#bpy-types-maskspline"), @@ -2142,7 +2197,7 @@ url_manual_mapping = ( ("bpy.types.bone.hide*", "animation/armatures/bones/properties/display.html#bpy-types-bone-hide"), ("bpy.types.colorramp*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp"), ("bpy.types.dopesheet*", "editors/dope_sheet/index.html#bpy-types-dopesheet"), - ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifier"), + ("bpy.types.fmodifier*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifier"), ("bpy.types.freestyle*", "render/freestyle/index.html#bpy-types-freestyle"), ("bpy.types.masklayer*", "movie_clip/masking/sidebar.html#bpy-types-masklayer"), ("bpy.types.movieclip*", "movie_clip/index.html#bpy-types-movieclip"), -- cgit v1.2.3 From 917a972b56af103aee406dfffe1f42745b5ad360 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Sep 2021 12:49:36 +1000 Subject: UI: keep navigation gizmos visible during modal operators Hiding viewport navigation gizmos caused the UI to "flicker" unnecessarily, the axis could also be useful as a reference. Resolves T73684 --- source/blender/editors/mesh/editmesh_bevel.c | 2 +- source/blender/editors/mesh/editmesh_bisect.c | 2 +- source/blender/editors/mesh/editmesh_inset.c | 2 +- source/blender/editors/transform/transform_generics.c | 2 +- source/blender/makesdna/DNA_view3d_types.h | 8 ++++++++ 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 01736f2919a..cdbbdd820b7 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -310,7 +310,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) if (v3d) { opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; + v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; } } diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index b511a1d2c14..f525f2c2e91 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -182,7 +182,7 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Misc other vars. */ G.moving = G_TRANSFORM_EDIT; opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; + v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; /* Initialize modal callout. */ ED_workspace_status_text(C, TIP_("LMB: Click and drag to draw cut line")); diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 18f51ae9df2..159e84db4ef 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -191,7 +191,7 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) G.moving = G_TRANSFORM_EDIT; if (v3d) { opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; + v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 0dfb229191c..b7f579cc12f 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -252,7 +252,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* turn gizmo off during transform */ if (t->flag & T_MODAL) { t->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag = V3D_GIZMO_HIDE; + v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; } if (t->scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) { diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4d88f6f0c15..fbf087ca392 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -610,6 +610,14 @@ enum { V3D_GIZMO_HIDE_TOOL = (1 << 3), }; +/** + * Hide these gizmos when modal operators are active, + * the intention is to hide all gizmos except for navigation since from a user-perspective + * these are closer to UI-level interface elements. Hiding them makes the UI flicker, also, + * the 3D view-axis can be useful to see during interactions. + */ +#define V3D_GIZMO_HIDE_DEFAULT_MODAL (V3D_GIZMO_HIDE_CONTEXT | V3D_GIZMO_HIDE_TOOL) + /** #View3d.gizmo_show_object */ enum { V3D_GIZMO_SHOW_OBJECT_TRANSLATE = (1 << 0), -- cgit v1.2.3 From 30845b5c8e042f42331b0c2a8a57ad05837b114e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Sep 2021 13:27:15 +1000 Subject: Doc: expand on comment for why bound-box access could cause issues --- source/blender/blenkernel/intern/constraint.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 72f14d94833..b9b15eba6a4 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3900,7 +3900,11 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar unit_m4(targetMatrix); INIT_MINMAX(curveMin, curveMax); - /* XXX(campbell): don't think this is good calling this here. */ + /* XXX(@campbellbarton): don't think this is good calling this here because + * the other object's data is lazily initializing bounding-box information. + * This could cause issues when evaluating from a thread. + * If the depsgraph ensures the bound-box is always available, a code-path could + * be used that doesn't lazy initialize to avoid thread safety issues in the future. */ BKE_object_minmax(ct->tar, curveMin, curveMax, true); /* get targetmatrix */ -- cgit v1.2.3 From 1c1be5bdf4fb9be0658b7d0b1ac13c563a003f1a Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 30 Aug 2021 15:36:39 +0200 Subject: Fix T90862: Texts in Outliner can have wrong icon In contrast to the Filebrowser, the Outliner (Blender File view) did not distinguish icons for text-based formats (if they have a filepath this can be done though). Maniphest Tasks: T90862 Differential Revision: https://developer.blender.org/D12347 --- source/blender/editors/space_outliner/outliner_draw.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index db37c8c1c8c..a094dfb0834 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -32,6 +32,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_text_types.h" #include "BLI_blenlib.h" #include "BLI_math.h" @@ -59,6 +60,7 @@ #include "DEG_depsgraph_build.h" #include "ED_armature.h" +#include "ED_fileselect.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -2625,9 +2627,17 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case ID_NLA: data.icon = ICON_NLA; break; - case ID_TXT: - data.icon = ICON_SCRIPT; + case ID_TXT: { + Text *text = (Text *)tselem->id; + if (text->filepath == NULL || (text->flags & TXT_ISMEM)) { + data.icon = ICON_FILE_TEXT; + } + else { + /* Helps distinguish text-based formats like the filebrowser does. */ + data.icon = ED_file_extension_icon(text->filepath); + } break; + } case ID_GR: data.icon = ICON_OUTLINER_COLLECTION; break; -- cgit v1.2.3 From 90a48fa06414ccf5fc5dd6092917413180ff30d1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 13:33:34 +0200 Subject: Geometry Nodes: fix memory leak for multi input sockets --- source/blender/nodes/NOD_geometry_exec.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index e0972e40a64..dbb5f8b240d 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -182,7 +182,7 @@ class GeoNodeExecParams { Vector values; for (GMutablePointer gvalue : gvalues) { if constexpr (is_stored_as_field_v) { - const Field &field = *gvalue.get>(); + const Field field = gvalue.relocate_out>(); values.append(fn::evaluate_constant_field(field)); } else { -- cgit v1.2.3 From fd60f6713a9d9e6f7d706b53bf1311f2f1cd9031 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 14:52:44 +0200 Subject: Functions: support optional outputs in multi-function Sometimes not all outputs of a multi-function are required by the caller. In those cases it would be a waste of compute resources to calculate the unused values anyway. Now, the caller of a multi-function can specify when a specific output is not used. The called function can check if an output is unused and may ignore it. Multi-functions can still computed unused outputs as before if they don't want to check if a specific output is unused. The multi-function procedure system has been updated to support ignored outputs in call instructions. An ignored output just has no variable assigned to it. The field system has been updated to generate a multi-function procedure where unused outputs are ignored. --- source/blender/blenlib/BLI_resource_scope.hh | 11 ++++ source/blender/functions/FN_multi_function.hh | 9 ++- .../blender/functions/FN_multi_function_params.hh | 68 ++++++++++++++++++++-- source/blender/functions/intern/field.cc | 46 +++++++++++---- .../functions/intern/multi_function_procedure.cc | 16 ++++- .../intern/multi_function_procedure_executor.cc | 31 +++++++--- source/blender/functions/tests/FN_field_test.cc | 16 +++++ .../functions/tests/FN_multi_function_test.cc | 27 +++++++++ .../tests/FN_multi_function_test_common.hh | 29 +++++++++ 9 files changed, 226 insertions(+), 27 deletions(-) diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index 6a98c2dcc1c..c3c0af4af50 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -136,6 +136,17 @@ class ResourceScope : NonCopyable, NonMovable { return this->construct(name, std::forward(value)); } + /** + * The passed in function will be called when the scope is destructed. + */ + template void add_destruct_call(Func func, const char *name) + { + void *buffer = m_allocator.allocate(sizeof(func), alignof(func)); + new (buffer) Func(std::move(func)); + this->add( + buffer, [](void *data) { (*(Func *)data)(); }, name); + } + /** * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory * allocated through this allocator will be freed when the collector is destructed. diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index f6c4addfb52..98788025558 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -121,8 +121,13 @@ class MultiFunction { } }; -inline MFParamsBuilder::MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size) - : MFParamsBuilder(fn.signature(), min_array_size) +inline MFParamsBuilder::MFParamsBuilder(const MultiFunction &fn, int64_t mask_size) + : MFParamsBuilder(fn.signature(), IndexMask(mask_size)) +{ +} + +inline MFParamsBuilder::MFParamsBuilder(const MultiFunction &fn, const IndexMask *mask) + : MFParamsBuilder(fn.signature(), *mask) { } diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 5af86c7c284..cae105e7c19 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -38,6 +38,7 @@ class MFParamsBuilder { private: ResourceScope scope_; const MFSignature *signature_; + IndexMask mask_; int64_t min_array_size_; Vector virtual_arrays_; Vector mutable_spans_; @@ -46,13 +47,18 @@ class MFParamsBuilder { friend class MFParams; - public: - MFParamsBuilder(const MFSignature &signature, int64_t min_array_size) - : signature_(&signature), min_array_size_(min_array_size) + MFParamsBuilder(const MFSignature &signature, const IndexMask mask) + : signature_(&signature), mask_(mask), min_array_size_(mask.min_array_size()) { } - MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size); + public: + MFParamsBuilder(const class MultiFunction &fn, int64_t size); + /** + * The indices referenced by the #mask has to live longer than the params builder. This is + * because the it might have to destruct elements for all masked indices in the end. + */ + MFParamsBuilder(const class MultiFunction &fn, const IndexMask *mask); template void add_readonly_single_input_value(T value, StringRef expected_name = "") { @@ -112,6 +118,17 @@ class MFParamsBuilder { BLI_assert(ref.size() >= min_array_size_); mutable_spans_.append(ref); } + void add_ignored_single_output(StringRef expected_name = "") + { + this->assert_current_param_name(expected_name); + const int param_index = this->current_param_index(); + const MFParamType ¶m_type = signature_->param_types[param_index]; + BLI_assert(param_type.category() == MFParamType::SingleOutput); + const CPPType &type = param_type.data_type().single_type(); + /* An empty span indicates that this is ignored. */ + const GMutableSpan dummy_span{type}; + mutable_spans_.append(dummy_span); + } void add_vector_output(GVectorArray &vector_array, StringRef expected_name = "") { @@ -176,6 +193,19 @@ class MFParamsBuilder { #endif } + void assert_current_param_name(StringRef expected_name) + { + UNUSED_VARS_NDEBUG(expected_name); +#ifdef DEBUG + if (expected_name.is_empty()) { + return; + } + const int param_index = this->current_param_index(); + StringRef actual_name = signature_->param_names[param_index]; + BLI_assert(actual_name == expected_name); +#endif + } + int current_param_index() const { return virtual_arrays_.size() + mutable_spans_.size() + virtual_vector_arrays_.size() + @@ -204,6 +234,19 @@ class MFParams { return *builder_->virtual_arrays_[data_index]; } + /** + * \return True when the caller provided a buffer for this output parameter. This allows the + * called multi-function to skip some computation. It is still valid to call + * #uninitialized_single_output when this returns false. In this case a new temporary buffer is + * allocated. + */ + bool single_output_is_required(int param_index, StringRef name = "") + { + this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + int data_index = builder_->signature_->data_index(param_index); + return !builder_->mutable_spans_[data_index].is_empty(); + } + template MutableSpan uninitialized_single_output(int param_index, StringRef name = "") { @@ -213,7 +256,22 @@ class MFParams { { this->assert_correct_param(param_index, name, MFParamType::SingleOutput); int data_index = builder_->signature_->data_index(param_index); - return builder_->mutable_spans_[data_index]; + GMutableSpan span = builder_->mutable_spans_[data_index]; + if (span.is_empty()) { + /* The output is ignored by the caller, but the multi-function does not handle this case. So + * create a temporary buffer that the multi-function can write to. */ + const CPPType &type = span.type(); + void *buffer = builder_->scope_.linear_allocator().allocate( + builder_->min_array_size_ * type.size(), type.alignment()); + if (!type.is_trivially_destructible()) { + /* Make sure the temporary elements will be destructed in the end. */ + builder_->scope_.add_destruct_call( + [&type, buffer, mask = builder_->mask_]() { type.destruct_indices(buffer, mask); }, + __func__); + } + span = GMutableSpan{type, buffer, builder_->min_array_size_}; + } + return span; } template diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 6133658d8e3..574a9e6284f 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -189,17 +189,43 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, field_with_index.current_input_index++; } else { - /* All inputs variables are ready, now add the function call. */ - Vector input_variables; - for (const GField &field : operation_inputs) { - input_variables.append(variable_by_field.lookup(field)); - } + /* All inputs variables are ready, now gather all variables that are used by the function + * and call it. */ const MultiFunction &multi_function = operation.multi_function(); - Vector output_variables = builder.add_call(multi_function, input_variables); - /* Add newly created variables to the map. */ - for (const int i : output_variables.index_range()) { - variable_by_field.add_new({operation, i}, output_variables[i]); + Vector variables(multi_function.param_amount()); + + int param_input_index = 0; + int param_output_index = 0; + for (const int param_index : multi_function.param_indices()) { + const MFParamType param_type = multi_function.param_type(param_index); + const MFParamType::InterfaceType interface_type = param_type.interface_type(); + if (interface_type == MFParamType::Input) { + const GField &input_field = operation_inputs[param_input_index]; + variables[param_index] = variable_by_field.lookup(input_field); + param_input_index++; + } + else if (interface_type == MFParamType::Output) { + const GFieldRef output_field{operation, param_output_index}; + const bool output_is_ignored = + field_tree_info.field_users.lookup(output_field).is_empty() && + !output_fields.contains(output_field); + if (output_is_ignored) { + /* Ignored outputs don't need a variable. */ + variables[param_index] = nullptr; + } + else { + /* Create a new variable for used outputs. */ + MFVariable &new_variable = procedure.new_variable(param_type.data_type()); + variables[param_index] = &new_variable; + variable_by_field.add_new(output_field, &new_variable); + } + param_output_index++; + } + else { + BLI_assert_unreachable(); + } } + builder.add_call_with_all_variables(multi_function, variables); } } } @@ -334,7 +360,7 @@ Vector evaluate_fields(ResourceScope &scope, build_multi_function_procedure_for_fields( procedure, scope, field_tree_info, varying_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; - MFParamsBuilder mf_params{procedure_executor, array_size}; + MFParamsBuilder mf_params{procedure_executor, &mask}; MFContextBuilder mf_context; /* Provide inputs to the procedure executor. */ diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index 2aa760a494f..fa95e8de71e 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -325,7 +325,14 @@ bool MFProcedure::validate_all_instruction_pointers_set() const bool MFProcedure::validate_all_params_provided() const { for (const MFCallInstruction *instruction : call_instructions_) { - for (const MFVariable *variable : instruction->params_) { + const MultiFunction &fn = instruction->fn(); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.category() == MFParamType::SingleOutput) { + /* Single outputs are optional. */ + continue; + } + const MFVariable *variable = instruction->params_[param_index]; if (variable == nullptr) { return false; } @@ -351,6 +358,9 @@ bool MFProcedure::validate_same_variables_in_one_call() const for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); const MFVariable *variable = instruction->params_[param_index]; + if (variable == nullptr) { + continue; + } for (const int other_param_index : fn.param_indices()) { if (other_param_index == param_index) { continue; @@ -681,7 +691,9 @@ class MFProcedureDotExport { if (instruction.prev().size() != 1) { return true; } - if (instruction.prev()[0].type() == MFInstructionCursor::Type::Branch) { + if (ELEM(instruction.prev()[0].type(), + MFInstructionCursor::Type::Branch, + MFInstructionCursor::Type::Entry)) { return true; } return false; diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc index 38b26415779..b97282accdd 100644 --- a/source/blender/functions/intern/multi_function_procedure_executor.cc +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -978,7 +978,7 @@ static bool evaluate_as_one(const MultiFunction &fn, return false; } for (VariableState *state : param_variable_states) { - if (!state->is_one()) { + if (state != nullptr && !state->is_one()) { return false; } } @@ -997,8 +997,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction, for (const int param_index : fn.param_indices()) { const MFVariable *variable = instruction.params()[param_index]; - VariableState &variable_state = variable_states.get_variable_state(*variable); - param_variable_states[param_index] = &variable_state; + if (variable == nullptr) { + param_variable_states[param_index] = nullptr; + } + else { + VariableState &variable_state = variable_states.get_variable_state(*variable); + param_variable_states[param_index] = &variable_state; + } } /* If all inputs to the function are constant, it's enough to call the function only once instead @@ -1008,19 +1013,29 @@ static void execute_call_instruction(const MFCallInstruction &instruction, for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); - VariableState &variable_state = *param_variable_states[param_index]; - variable_states.add_as_param__one(variable_state, params, param_type, mask); + VariableState *variable_state = param_variable_states[param_index]; + if (variable_state == nullptr) { + params.add_ignored_single_output(); + } + else { + variable_states.add_as_param__one(*variable_state, params, param_type, mask); + } } fn.call(IndexRange(1), params, context); } else { - MFParamsBuilder params(fn, mask.min_array_size()); + MFParamsBuilder params(fn, &mask); for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); - VariableState &variable_state = *param_variable_states[param_index]; - variable_states.add_as_param(variable_state, params, param_type, mask); + VariableState *variable_state = param_variable_states[param_index]; + if (variable_state == nullptr) { + params.add_ignored_single_output(); + } + else { + variable_states.add_as_param(*variable_state, params, param_type, mask); + } } fn.call(mask, params, context); diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc index 212b79e75d3..1c2d5c8eaad 100644 --- a/source/blender/functions/tests/FN_field_test.cc +++ b/source/blender/functions/tests/FN_field_test.cc @@ -5,6 +5,7 @@ #include "FN_cpp_type.hh" #include "FN_field.hh" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" namespace blender::fn::tests { @@ -275,4 +276,19 @@ TEST(field, SameFieldTwice) EXPECT_EQ(varray2->get(1), 10); } +TEST(field, IgnoredOutput) +{ + static OptionalOutputsFunction fn; + Field field{std::make_shared(fn), 0}; + + FieldContext field_context; + FieldEvaluator field_evaluator{field_context, 10}; + const VArray *results = nullptr; + field_evaluator.add(field, &results); + field_evaluator.evaluate(); + + EXPECT_EQ(results->get(0), 5); + EXPECT_EQ(results->get(3), 5); +} + } // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 204dbfab6aa..d99993b35ac 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -328,5 +328,32 @@ TEST(multi_function, CustomMF_Convert) EXPECT_EQ(outputs[2], 9); } +TEST(multi_function, IgnoredOutputs) +{ + OptionalOutputsFunction fn; + { + MFParamsBuilder params(fn, 10); + params.add_ignored_single_output("Out 1"); + params.add_ignored_single_output("Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + } + { + Array results_1(10); + Array results_2(10, NoInitialization()); + + MFParamsBuilder params(fn, 10); + params.add_uninitialized_single_output(results_1.as_mutable_span(), "Out 1"); + params.add_uninitialized_single_output(results_2.as_mutable_span(), "Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + + EXPECT_EQ(results_1[0], 5); + EXPECT_EQ(results_1[3], 5); + EXPECT_EQ(results_1[9], 5); + EXPECT_EQ(results_2[0], "hello, this is a long string"); + } +} + } // namespace } // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh index 51c8fac8a96..2a64cc302fe 100644 --- a/source/blender/functions/tests/FN_multi_function_test_common.hh +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -171,4 +171,33 @@ class SumVectorFunction : public MultiFunction { } }; +class OptionalOutputsFunction : public MultiFunction { + public: + OptionalOutputsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Optional Outputs"}; + signature.single_output("Out 1"); + signature.single_output("Out 2"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + if (params.single_output_is_required(0, "Out 1")) { + MutableSpan values = params.uninitialized_single_output(0, "Out 1"); + values.fill_indices(mask, 5); + } + MutableSpan values = params.uninitialized_single_output(1, "Out 2"); + for (const int i : mask) { + new (&values[i]) std::string("hello, this is a long string"); + } + } +}; + } // namespace blender::fn::tests -- cgit v1.2.3 From 426e2663a0891d16a497a33b273a5cee1e09f929 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 15:18:06 +0200 Subject: Fix: use type name instead of variable name That was a typo in rBfd60f6713a9d9e6f7d706b53bf1311f2f1cd9031. --- source/blender/blenlib/BLI_resource_scope.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index c3c0af4af50..b7720b52ecc 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -141,7 +141,7 @@ class ResourceScope : NonCopyable, NonMovable { */ template void add_destruct_call(Func func, const char *name) { - void *buffer = m_allocator.allocate(sizeof(func), alignof(func)); + void *buffer = m_allocator.allocate(sizeof(Func), alignof(Func)); new (buffer) Func(std::move(func)); this->add( buffer, [](void *data) { (*(Func *)data)(); }, name); -- cgit v1.2.3 From dee0b56b9216de8f37589b15be2d21cc1b946773 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 16:08:09 +0200 Subject: Cleanup: simplify resource scope methods Previously, a debug name had to be passed to all methods that added a resource to the `ResourceScope`. The idea was that this would make it easier to find certain bugs. In reality I never found this to be useful, and it was mostly annoying. The thing is, something that is in a resource scope never leaks (unless the resource scope is not destructed of course). Removing the name parameter makes the structure easier to use. --- .../blender/blenkernel/intern/attribute_access.cc | 4 +- source/blender/blenlib/BLI_resource_scope.hh | 77 +++++++--------------- .../editors/space_spreadsheet/space_spreadsheet.cc | 2 +- .../spreadsheet_data_source_geometry.cc | 2 +- .../space_spreadsheet/spreadsheet_row_filter.cc | 2 +- source/blender/functions/FN_field.hh | 14 ++-- .../blender/functions/FN_multi_function_params.hh | 30 ++++----- source/blender/functions/intern/field.cc | 20 +++--- source/blender/functions/tests/FN_field_test.cc | 2 +- source/blender/nodes/NOD_multi_function.hh | 2 +- .../nodes/geometry/nodes/node_geo_input_index.cc | 2 +- .../nodes/geometry/nodes/node_geo_input_normal.cc | 8 +-- 12 files changed, 67 insertions(+), 98 deletions(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 2a5bb99a18b..bdf1891a55a 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -1315,7 +1315,7 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex const AttributeDomain domain = geometry_context->domain(); const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type); - return scope.add(std::move(attribute), __func__); + return scope.add(std::move(attribute)); } return nullptr; } @@ -1350,7 +1350,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( const CustomDataType data_type = cpp_type_to_custom_data_type(*type_); GVArrayPtr attribute = component.attribute_try_get_for_read( anonymous_id_.get(), domain, data_type); - return scope.add(std::move(attribute), __func__); + return scope.add(std::move(attribute)); } return nullptr; } diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index b7720b52ecc..edffb148477 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -50,11 +50,10 @@ class ResourceScope : NonCopyable, NonMovable { struct ResourceData { void *data; void (*free)(void *data); - const char *debug_name; }; - LinearAllocator<> m_allocator; - Vector m_resources; + LinearAllocator<> allocator_; + Vector resources_; public: ResourceScope() = default; @@ -62,8 +61,8 @@ class ResourceScope : NonCopyable, NonMovable { ~ResourceScope() { /* Free in reversed order. */ - for (int64_t i = m_resources.size(); i--;) { - ResourceData &data = m_resources[i]; + for (int64_t i = resources_.size(); i--;) { + ResourceData &data = resources_[i]; data.free(data.data); } } @@ -72,20 +71,17 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when * the collector is destructed. */ - template T *add(std::unique_ptr resource, const char *name) + template T *add(std::unique_ptr resource) { BLI_assert(resource.get() != nullptr); T *ptr = resource.release(); if (ptr == nullptr) { return nullptr; } - this->add( - ptr, - [](void *data) { - T *typed_data = reinterpret_cast(data); - delete typed_data; - }, - name); + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + delete typed_data; + }); return ptr; } @@ -93,7 +89,7 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of the resource to the ResourceScope. It will be destructed when the * collector is destructed. */ - template T *add(destruct_ptr resource, const char *name) + template T *add(destruct_ptr resource) { T *ptr = resource.release(); if (ptr == nullptr) { @@ -104,13 +100,10 @@ class ResourceScope : NonCopyable, NonMovable { return ptr; } - this->add( - ptr, - [](void *data) { - T *typed_data = reinterpret_cast(data); - typed_data->~T(); - }, - name); + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + typed_data->~T(); + }); return ptr; } @@ -118,33 +111,31 @@ class ResourceScope : NonCopyable, NonMovable { * Pass ownership of some resource to the ResourceScope. The given free function will be * called when the collector is destructed. */ - void add(void *userdata, void (*free)(void *), const char *name) + void add(void *userdata, void (*free)(void *)) { ResourceData data; - data.debug_name = name; data.data = userdata; data.free = free; - m_resources.append(data); + resources_.append(data); } /** * Construct an object with the same value in the ResourceScope and return a reference to the * new value. */ - template T &add_value(T &&value, const char *name) + template T &add_value(T &&value) { - return this->construct(name, std::forward(value)); + return this->construct(std::forward(value)); } /** * The passed in function will be called when the scope is destructed. */ - template void add_destruct_call(Func func, const char *name) + template void add_destruct_call(Func func) { - void *buffer = m_allocator.allocate(sizeof(Func), alignof(Func)); + void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); new (buffer) Func(std::move(func)); - this->add( - buffer, [](void *data) { (*(Func *)data)(); }, name); + this->add(buffer, [](void *data) { (*(Func *)data)(); }); } /** @@ -153,37 +144,19 @@ class ResourceScope : NonCopyable, NonMovable { */ LinearAllocator<> &linear_allocator() { - return m_allocator; + return allocator_; } /** * Utility method to construct an instance of type T that will be owned by the ResourceScope. */ - template T &construct(const char *name, Args &&...args) + template T &construct(Args &&...args) { - destruct_ptr value_ptr = m_allocator.construct(std::forward(args)...); + destruct_ptr value_ptr = allocator_.construct(std::forward(args)...); T &value_ref = *value_ptr; - this->add(std::move(value_ptr), name); + this->add(std::move(value_ptr)); return value_ref; } - - /** - * Print the names of all the resources that are owned by this ResourceScope. This can be - * useful for debugging. - */ - void print(StringRef name) const - { - if (m_resources.size() == 0) { - std::cout << "\"" << name << "\" has no resources.\n"; - return; - } - else { - std::cout << "Resources for \"" << name << "\":\n"; - for (const ResourceData &data : m_resources) { - std::cout << " " << data.data << ": " << data.debug_name << '\n'; - } - } - } }; } // namespace blender diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index d503297f540..a82648aeee0 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -370,7 +370,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) std::unique_ptr values_ptr = data_source->get_column_values(*column->id); /* Should have been removed before if it does not exist anymore. */ BLI_assert(values_ptr); - const ColumnValues *values = scope.add(std::move(values_ptr), __func__); + const ColumnValues *values = scope.add(std::move(values_ptr)); const int width = get_column_width_in_pixels(*values); spreadsheet_layout.columns.append({values, width}); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index f5fef5e4486..bd2d89e4f27 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -70,7 +70,7 @@ std::unique_ptr GeometryDataSource::get_column_values( if (!attribute) { return {}; } - const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__); + const fn::GVArray *varray = scope_.add(std::move(attribute.varray)); if (attribute.domain != domain_) { return {}; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc index ae336edfead..1e46fef8d71 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc @@ -328,7 +328,7 @@ Span spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet, geometry_data_source->apply_selection_filter(rows_included); } - Vector &indices = scope.construct>(__func__); + Vector &indices = scope.construct>(); index_vector_from_bools(rows_included, indices); return indices; diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index d6259bce435..d4375b625ce 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -381,7 +381,7 @@ class FieldEvaluator : NonMovable, NonCopyable { /** Same as #add_with_destination but typed. */ template int add_with_destination(Field field, VMutableArray &dst) { - GVMutableArray &varray = scope_.construct>(__func__, dst); + GVMutableArray &varray = scope_.construct>(dst); return this->add_with_destination(GField(std::move(field)), varray); } @@ -401,7 +401,7 @@ class FieldEvaluator : NonMovable, NonCopyable { */ template int add_with_destination(Field field, MutableSpan dst) { - GVMutableArray &varray = scope_.construct>(__func__, dst); + GVMutableArray &varray = scope_.construct>(dst); return this->add_with_destination(std::move(field), varray); } @@ -417,10 +417,10 @@ class FieldEvaluator : NonMovable, NonCopyable { { const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field)); dst_varrays_.append(nullptr); - output_pointer_infos_.append(OutputPointerInfo{ - varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &scope) { - *(const VArray **)dst = &*scope.construct>(__func__, varray); - }}); + output_pointer_infos_.append( + OutputPointerInfo{varray_ptr, [](void *dst, const GVArray &varray, ResourceScope &scope) { + *(const VArray **)dst = &*scope.construct>(varray); + }}); return field_index; } @@ -443,7 +443,7 @@ class FieldEvaluator : NonMovable, NonCopyable { template const VArray &get_evaluated(const int field_index) { const GVArray &varray = this->get_evaluated(field_index); - GVArray_Typed &typed_varray = scope_.construct>(__func__, varray); + GVArray_Typed &typed_varray = scope_.construct>(varray); return *typed_varray; } diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index cae105e7c19..fe4d2b90d80 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -62,25 +62,24 @@ class MFParamsBuilder { template void add_readonly_single_input_value(T value, StringRef expected_name = "") { - T *value_ptr = &scope_.add_value(std::move(value), __func__); + T *value_ptr = &scope_.add_value(std::move(value)); this->add_readonly_single_input(value_ptr, expected_name); } template void add_readonly_single_input(const T *value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct( - __func__, CPPType::get(), min_array_size_, value), - expected_name); + this->add_readonly_single_input( + scope_.construct(CPPType::get(), min_array_size_, value), + expected_name); } void add_readonly_single_input(const GSpan span, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct(__func__, span), - expected_name); + this->add_readonly_single_input(scope_.construct(span), expected_name); } void add_readonly_single_input(GPointer value, StringRef expected_name = "") { - this->add_readonly_single_input(scope_.construct( - __func__, *value.type(), min_array_size_, value.get()), - expected_name); + this->add_readonly_single_input( + scope_.construct(*value.type(), min_array_size_, value.get()), + expected_name); } void add_readonly_single_input(const GVArray &ref, StringRef expected_name = "") { @@ -91,13 +90,13 @@ class MFParamsBuilder { void add_readonly_vector_input(const GVectorArray &vector_array, StringRef expected_name = "") { - this->add_readonly_vector_input( - scope_.construct(__func__, vector_array), expected_name); + this->add_readonly_vector_input(scope_.construct(vector_array), + expected_name); } void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "") { this->add_readonly_vector_input( - scope_.construct(__func__, single_vector, min_array_size_), + scope_.construct(single_vector, min_array_size_), expected_name); } void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "") @@ -225,7 +224,7 @@ class MFParams { template const VArray &readonly_single_input(int param_index, StringRef name = "") { const GVArray &array = this->readonly_single_input(param_index, name); - return builder_->scope_.construct>(__func__, array); + return builder_->scope_.construct>(array); } const GVArray &readonly_single_input(int param_index, StringRef name = "") { @@ -266,8 +265,7 @@ class MFParams { if (!type.is_trivially_destructible()) { /* Make sure the temporary elements will be destructed in the end. */ builder_->scope_.add_destruct_call( - [&type, buffer, mask = builder_->mask_]() { type.destruct_indices(buffer, mask); }, - __func__); + [&type, buffer, mask = builder_->mask_]() { type.destruct_indices(buffer, mask); }); } span = GMutableSpan{type, buffer, builder_->min_array_size_}; } @@ -278,7 +276,7 @@ class MFParams { const VVectorArray &readonly_vector_input(int param_index, StringRef name = "") { const GVVectorArray &vector_array = this->readonly_vector_input(param_index, name); - return builder_->scope_.construct>(__func__, vector_array); + return builder_->scope_.construct>(vector_array); } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 574a9e6284f..43f28efd002 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -92,7 +92,7 @@ static Vector get_field_context_inputs( if (varray == nullptr) { const CPPType &type = field_input.cpp_type(); varray = &scope.construct( - __func__, type, mask.min_array_size(), type.default_value()); + type, mask.min_array_size(), type.default_value()); } field_context_inputs.append(varray); } @@ -237,8 +237,8 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, if (!already_output_variables.add(variable)) { /* One variable can be output at most once. To output the same value twice, we have to make * a copy first. */ - const MultiFunction ©_fn = scope.construct( - __func__, "copy", variable->data_type()); + const MultiFunction ©_fn = scope.construct("copy", + variable->data_type()); variable = builder.add_call<1>(copy_fn, {variable})[0]; } builder.add_output_parameter(*variable); @@ -381,14 +381,13 @@ Vector evaluate_fields(ResourceScope &scope, buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment()); /* Make sure that elements in the buffer will be destructed. */ - PartiallyInitializedArray &destruct_helper = scope.construct( - __func__); + PartiallyInitializedArray &destruct_helper = scope.construct(); destruct_helper.buffer = buffer; destruct_helper.mask = mask; destruct_helper.type = &type; r_varrays[out_index] = &scope.construct( - __func__, GSpan{type, buffer, array_size}); + GSpan{type, buffer, array_size}); } else { /* Write the result into the existing span. */ @@ -427,8 +426,7 @@ Vector evaluate_fields(ResourceScope &scope, void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment()); /* Use this to make sure that the value is destructed in the end. */ - PartiallyInitializedArray &destruct_helper = scope.construct( - __func__); + PartiallyInitializedArray &destruct_helper = scope.construct(); destruct_helper.buffer = buffer; destruct_helper.mask = IndexRange(1); destruct_helper.type = &type; @@ -439,7 +437,7 @@ Vector evaluate_fields(ResourceScope &scope, /* Create virtual array that can be used after the procedure has been executed below. */ const int out_index = constant_field_indices[i]; r_varrays[out_index] = &scope.construct( - __func__, type, array_size, buffer); + type, array_size, buffer); } procedure_executor.call(IndexRange(1), mf_params, mf_context); @@ -608,7 +606,7 @@ int FieldEvaluator::add_with_destination(GField field, GVMutableArray &dst) int FieldEvaluator::add_with_destination(GField field, GMutableSpan dst) { - GVMutableArray &varray = scope_.construct(__func__, dst); + GVMutableArray &varray = scope_.construct(dst); return this->add_with_destination(std::move(field), varray); } @@ -661,7 +659,7 @@ IndexMask FieldEvaluator::get_evaluated_as_mask(const int field_index) return IndexRange(0); } - return scope_.add_value(indices_from_selection(*typed_varray), __func__).as_span(); + return scope_.add_value(indices_from_selection(*typed_varray)).as_span(); } } // namespace blender::fn diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc index 1c2d5c8eaad..041cdbd0f00 100644 --- a/source/blender/functions/tests/FN_field_test.cc +++ b/source/blender/functions/tests/FN_field_test.cc @@ -41,7 +41,7 @@ class IndexFieldInput final : public FieldInput { auto index_func = [](int i) { return i; }; return &scope.construct< GVArray_For_EmbeddedVArray>>( - __func__, mask.min_array_size(), mask.min_array_size(), index_func); + mask.min_array_size(), mask.min_array_size(), index_func); } }; diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 2f4b104fb4c..58816544dc1 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -114,7 +114,7 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) template inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) { - const T &fn = resource_scope_.construct(__func__, std::forward(args)...); + const T &fn = resource_scope_.construct(std::forward(args)...); this->set_matching_fn(&fn); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index e2287abe56c..c52ff3d448e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -37,7 +37,7 @@ class IndexFieldInput final : public fn::FieldInput { auto index_func = [](int i) { return i; }; return &scope.construct< fn::GVArray_For_EmbeddedVArray>>( - __func__, mask.min_array_size(), mask.min_array_size(), index_func); + mask.min_array_size(), mask.min_array_size(), index_func); } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index b8f126ef1db..07818f2a3ad 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -104,10 +104,10 @@ static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_c switch (domain) { case ATTR_DOMAIN_FACE: { - return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask), __func__).get(); + return scope.add_value(mesh_face_normals(mesh, verts, polys, loops, mask)).get(); } case ATTR_DOMAIN_POINT: { - return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask), __func__).get(); + return scope.add_value(mesh_vertex_normals(mesh, verts, polys, loops, mask)).get(); } case ATTR_DOMAIN_EDGE: { /* In this case, start with vertex normals and convert to the edge domain, since the @@ -128,7 +128,7 @@ static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_c } return &scope.construct>>( - __func__, std::move(edge_normals)); + std::move(edge_normals)); } case ATTR_DOMAIN_CORNER: { /* The normals on corners are just the mesh's face normals, so start with the face normal @@ -140,7 +140,7 @@ static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_c * will still be normalized, since the face normal is just copied to every corner. */ GVArrayPtr loop_normals = mesh_component.attribute_try_adapt_domain( std::move(face_normals), ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); - return scope.add_value(std::move(loop_normals), __func__).get(); + return scope.add_value(std::move(loop_normals)).get(); } default: return nullptr; -- cgit v1.2.3 From edaeec3e720cacf789d0b3c438df6c2944adbd8b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 16:34:31 +0200 Subject: Nodes: cache node declaration on node Previously, it was necessary to rebuild the node declaration every time it was used. Now it is cached per node for easy and fast access. For more details on what this is, look at the comment in `DNA_node_types.h`. Differential Revision: https://developer.blender.org/D12471 --- source/blender/blenkernel/BKE_node.h | 2 ++ source/blender/blenkernel/intern/node.cc | 28 ++++++++++++++++++++++---- source/blender/makesdna/DNA_node_types.h | 30 ++++++++++++++++++++++++++++ source/blender/nodes/NOD_node_declaration.hh | 2 ++ source/blender/nodes/intern/node_socket.cc | 8 +++----- 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 9df3d7c544d..d4bc0245a61 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -731,6 +731,8 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available); int nodeSocketLinkLimit(const struct bNodeSocket *sock); +void nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node); + /* Node Clipboard */ void BKE_node_clipboard_init(const struct bNodeTree *ntree); void BKE_node_clipboard_clear(void); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e6af587cbd7..61b90b44b03 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -653,6 +653,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) BLO_read_list(reader, &ntree->nodes); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->typeinfo = nullptr; + node->declaration = nullptr; BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); @@ -1014,10 +1015,8 @@ IDTypeInfo IDType_ID_NT = { static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) { if (ntype->declare != nullptr) { - blender::nodes::NodeDeclaration node_decl; - blender::nodes::NodeDeclarationBuilder builder{node_decl}; - ntype->declare(builder); - node_decl.build(*ntree, *node); + nodeDeclarationEnsure(ntree, node); + node->declaration->build(*ntree, *node); return; } bNodeSocketTemplate *sockdef; @@ -2216,6 +2215,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, bNodeLink *link_dst, *link_src; *node_dst = *node_src; + + /* Reset the declaration of the new node. */ + node_dst->declaration = nullptr; + /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { if (unique_name) { @@ -3103,6 +3106,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node->prop); } + delete node->declaration; + MEM_freeN(node); if (ntree) { @@ -3933,6 +3938,21 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) return sock->limit; } +/** + * If the node implements a `declare` function, this function makes sure that `node->declaration` + * is up to date. + */ +void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node) +{ + if (node->typeinfo->declare == nullptr) { + return; + } + + node->declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; + node->typeinfo->declare(builder); +} + /* ************** Node Clipboard *********** */ #define USE_NODE_CB_VALIDATE diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 82eabf6995c..c4cbc71762c 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -216,6 +216,16 @@ typedef enum eNodeSocketFlag { SOCK_HIDE_LABEL = (1 << 12), } eNodeSocketFlag; +/** Workaround to forward-declare C++ type in C header. */ +#ifdef __cplusplus +namespace blender::nodes { +class NodeDeclaration; +} +using NodeDeclarationHandle = blender::nodes::NodeDeclaration; +#else +typedef struct NodeDeclarationHandle NodeDeclarationHandle; +#endif + /* TODO: Limit data in bNode to what we want to see saved. */ typedef struct bNode { struct bNode *next, *prev, *new_node; @@ -315,6 +325,26 @@ typedef struct bNode { * needs to be a float to feed GPU_uniform. */ float sss_id; + + /** + * Describes the desired interface of the node. This is run-time data only. + * The actual interface of the node may deviate from the declaration temporarily. + * It's possible to sync the actual state of the node to the desired state. Currently, this is + * only done when a node is created or loaded. + * + * In the future, we may want to keep more data only in the declaration, so that it does not have + * to be synced to other places that are stored in files. That especially applies to data that + * can't be edited by users directly (e.g. min/max values of sockets, tooltips, ...). + * + * The declaration of a node can be recreated at any time when it is used. Caching it here is + * just a bit more efficient when it is used a lot. To make sure that the cache is up-to-date, + * call #nodeDeclarationEnsure before using it. + * + * Currently, the declaration is the same for every node of the same type. Going forward, that is + * intended to change though. Especially when nodes become more dynamic with respect to how many + * sockets they have. + */ + NodeDeclarationHandle *declaration; } bNode; /* node->flag */ diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 52f4ac291d2..7ba4ac52b86 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -60,6 +60,8 @@ class NodeDeclaration { Span inputs() const; Span outputs() const; + + MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") }; class NodeDeclarationBuilder { diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 8efd6c55459..31260f95242 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -269,11 +269,9 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) return; } if (ntype->declare != nullptr) { - blender::nodes::NodeDeclaration node_decl; - blender::nodes::NodeDeclarationBuilder builder{node_decl}; - ntype->declare(builder); - if (!node_decl.matches(*node)) { - refresh_node(*ntree, *node, node_decl, do_id_user); + nodeDeclarationEnsure(ntree, node); + if (!node->declaration->matches(*node)) { + refresh_node(*ntree, *node, *node->declaration, do_id_user); } return; } -- cgit v1.2.3 From 2d13c823ee1c336e3c8cbd01f8b3b21672bfd0e4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Sep 2021 16:41:29 +0200 Subject: Cleanup: fix inconsistent parameter name Found by clang-tidy. --- source/blender/blenkernel/BKE_packedFile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index 9b66c6003d7..8cb0c78d9aa 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -77,7 +77,7 @@ char *BKE_packedfile_unpack_to_file(struct ReportList *reports, char *BKE_packedfile_unpack(struct Main *bmain, struct ReportList *reports, struct ID *id, - const char *orig_file_name, + const char *orig_file_path, struct PackedFile *pf, enum ePF_FileStatus how); int BKE_packedfile_unpack_vfont(struct Main *bmain, -- cgit v1.2.3 From cddb7920210f10b4c4b8595f80499cbbc5d5f57c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 14 Sep 2021 17:43:41 +0200 Subject: ID management: Add new version of `relink_to_newid` using proper new remapping code. Current `BKE_libblock_relink_to_newid` is using its own simplistic, limited and not really correct version of ID remapping. While doing a full replacement would have been ideal, this is risky/time-constrained for Blender 3.0 release, so for now we'll have both versions co-existing. --- source/blender/blenkernel/BKE_lib_remap.h | 1 + source/blender/blenkernel/intern/lib_remap.c | 53 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index c90a284c204..c70521f9593 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -112,6 +112,7 @@ void BKE_libblock_relink_ex(struct Main *bmain, const short remap_flags) ATTR_NONNULL(1, 2); void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index bba15a3bcdf..250b8d4d515 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -699,6 +699,9 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) * * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... + * + * WARNING: This is a deprecated version of this function, should not be used by new code. See + * #BKE_libblock_relink_to_newid_new below. */ void BKE_libblock_relink_to_newid(ID *id) { @@ -708,3 +711,53 @@ void BKE_libblock_relink_to_newid(ID *id) BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); } + +/* ************************ + * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this + * #BKE_libblock_relink_to_newid_new new code and remove old one. + ************************** */ +static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) +{ + const int cb_flag = cb_data->cb_flag; + if (cb_flag & IDWALK_CB_EMBEDDED) { + return IDWALK_RET_NOP; + } + + Main *bmain = cb_data->bmain; + ID *id_owner = cb_data->id_owner; + ID **id_pointer = cb_data->id_pointer; + ID *id = *id_pointer; + if (id) { + /* See: NEW_ID macro */ + if (id->newid != NULL) { + BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + id = id->newid; + } + if (id->tag & LIB_TAG_NEW) { + id->tag &= ~LIB_TAG_NEW; + BKE_libblock_relink_to_newid_new(bmain, id); + } + } + return IDWALK_RET_NOP; +} + +/** + * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively + * in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`. + * + * NOTE: `LIB_TAG_NEW` is cleared + * + * Very specific usage, not sure we'll keep it on the long run, + * currently only used in Object/Collection duplication code... + */ +void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) +{ + if (ID_IS_LINKED(id)) { + return; + } + /* We do not want to have those cached relationship data here. */ + BLI_assert(bmain->relations == NULL); + + id->tag &= ~LIB_TAG_NEW; + BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); +} -- cgit v1.2.3 From 3be5ce4aad5e4bbc25474511e56036d6980e1cea Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 14 Sep 2021 17:49:57 +0200 Subject: LibLink: New Append code. This commit fully refactors the way linked IDs are made local when appended. Previously, `BKE_library_make_local` was (ab)used for this task, but it was missing some contextual data and doing complex processing to try to work around this, with limited sucess. Further more, it was nearly impossibe to extend (e.g. to get new append behaviors necessary for the asset project). The new code is a dedicated append step in WM linking process. NOTE: BPY API (`libray.load()` context manager) uses its own code here, which still relies on `BKE_library_make_local` for appending. Unfortunately, merging those two different code paths is not trivial so for now this API will remain unchanged. Fix T55629: Append already linked Data is impossible. --- source/blender/blenloader/intern/versioning_280.c | 2 +- source/blender/makesdna/DNA_space_types.h | 4 +- .../blender/windowmanager/intern/wm_files_link.c | 677 +++++++++++++++++++-- 3 files changed, 636 insertions(+), 47 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 2598c53a5e0..bf0463432db 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3416,7 +3416,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_FILE: { SpaceFile *sfile = (SpaceFile *)sl; if (sfile->params) { - sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_6 | + sfile->params->flag &= ~(FILE_APPEND_SET_FAKEUSER | FILE_APPEND_RECURSIVE | FILE_OBDATA_INSTANCE); } break; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 863c53615c1..13e6904662e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -936,13 +936,13 @@ typedef enum eFileSel_Action { * (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h). */ typedef enum eFileSel_Params_Flag { - FILE_PARAMS_FLAG_UNUSED_1 = (1 << 0), /* cleared */ + FILE_APPEND_SET_FAKEUSER = (1 << 0), FILE_RELPATH = (1 << 1), FILE_LINK = (1 << 2), FILE_HIDE_DOT = (1 << 3), FILE_AUTOSELECT = (1 << 4), FILE_ACTIVE_COLLECTION = (1 << 5), - FILE_PARAMS_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + FILE_APPEND_RECURSIVE = (1 << 6), FILE_DIRSEL_ONLY = (1 << 7), FILE_FILTER = (1 << 8), FILE_OBDATA_INSTANCE = (1 << 9), diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 606c9252ff9..4affabb6da2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -35,7 +35,9 @@ #include "MEM_guardedalloc.h" #include "DNA_ID.h" +#include "DNA_collection_types.h" #include "DNA_key_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_windowmanager_types.h" @@ -50,15 +52,21 @@ #include "BLO_readfile.h" +#include "BKE_armature.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" +#include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" #include "BKE_report.h" +#include "BKE_rigidbody.h" +#include "BKE_scene.h" #include "BKE_idtype.h" @@ -137,6 +145,14 @@ static short wm_link_append_flag(wmOperator *op) if (RNA_boolean_get(op->ptr, "link")) { flag |= FILE_LINK; } + else { + if (RNA_boolean_get(op->ptr, "use_recursive")) { + flag |= FILE_APPEND_RECURSIVE; + } + if (RNA_boolean_get(op->ptr, "set_fake")) { + flag |= FILE_APPEND_SET_FAKEUSER; + } + } if (RNA_boolean_get(op->ptr, "instance_collections")) { flag |= FILE_COLLECTION_INSTANCE; } @@ -153,6 +169,10 @@ typedef struct WMLinkAppendDataItem { *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ short idcode; + /** Type of action to do to append this item, and other append-specific information. */ + char append_action; + char append_tag; + ID *new_id; void *customdata; } WMLinkAppendDataItem; @@ -167,10 +187,32 @@ typedef struct WMLinkAppendData { */ int flag; + /** Allows to easily find an existing items from an ID pointer. Used by append code. */ + GHash *new_id_to_item; + /* Internal 'private' data */ MemArena *memarena; } WMLinkAppendData; +typedef struct WMLinkAppendDataCallBack { + WMLinkAppendData *lapp_data; + WMLinkAppendDataItem *item; + ReportList *reports; + +} WMLinkAppendDataCallBack; + +enum { + WM_APPEND_ACT_UNSET = 0, + WM_APPEND_ACT_KEEP_LINKED, + WM_APPEND_ACT_REUSE_LOCAL, + WM_APPEND_ACT_MAKE_LOCAL, + WM_APPEND_ACT_COPY_LOCAL, +}; + +enum { + WM_APPEND_TAG_INDIRECT = 1 << 0, +}; + static WMLinkAppendData *wm_link_append_data_new(const int flag) { MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -184,6 +226,10 @@ static WMLinkAppendData *wm_link_append_data_new(const int flag) static void wm_link_append_data_free(WMLinkAppendData *lapp_data) { + if (lapp_data->new_id_to_item != NULL) { + BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL); + } + BLI_memarena_free(lapp_data->memarena); } @@ -213,6 +259,7 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); item->new_id = NULL; + item->append_action = WM_APPEND_ACT_UNSET; item->customdata = customdata; BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); @@ -221,6 +268,567 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp return item; } +/* -------------------------------------------------------------------- */ +/** \name Library appending helper functions. + * + * FIXME: Deduplicate code with similar one in readfile.c + * \{ */ + +static bool object_in_any_scene(Main *bmain, Object *ob) +{ + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { + if (BKE_scene_object_find(sce, ob)) { + return true; + } + } + + return false; +} + +static bool object_in_any_collection(Main *bmain, Object *ob) +{ + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob)) { + return true; + } + } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->master_collection != NULL && + BKE_collection_has_object(scene->master_collection, ob)) { + return true; + } + } + + return false; +} + +/** + * Shared operations to perform on the object's base after adding it to the scene. + */ +static void wm_append_loose_data_instantiate_object_base_instance_init( + Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) +{ + Base *base = BKE_view_layer_base_find(view_layer, ob); + + if (v3d != NULL) { + base->local_view_bits |= v3d->local_view_uuid; + } + + if (set_selected) { + if (base->flag & BASE_SELECTABLE) { + base->flag |= BASE_SELECTED; + } + } + + if (set_active) { + view_layer->basact = base; + } + + BKE_scene_object_base_flag_sync_from_base(base); +} + +static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item) +{ + /* We consider that if we either kept it linked, or re-used already local data, instantiation + * status of those should not be modified. */ + if (!ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_MAKE_LOCAL)) { + return NULL; + } + + ID *id = item->new_id; + if (id == NULL) { + return NULL; + } + + if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + BLI_assert(ID_IS_LINKED(id)); + id = id->newid; + if (id == NULL) { + return NULL; + } + + BLI_assert(!ID_IS_LINKED(id)); + return id; + } + + BLI_assert(!ID_IS_LINKED(id)); + return id; +} + +static void wm_append_loose_data_instantiate_ensure_active_collection( + WMLinkAppendData *lapp_data, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + Collection **r_active_collection) +{ + /* Find or add collection as needed. */ + if (*r_active_collection == NULL) { + if (lapp_data->flag & FILE_ACTIVE_COLLECTION) { + LayerCollection *lc = BKE_layer_collection_get_active(view_layer); + *r_active_collection = lc->collection; + } + else { + *r_active_collection = BKE_collection_add(bmain, scene->master_collection, NULL); + } + } +} + +/* TODO: De-duplicate this code with the one in readfile.c, think we need some utils code for that + * in BKE. */ +static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const View3D *v3d) +{ + LinkNode *itemlink; + Collection *active_collection = NULL; + const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0; + + const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; + /* Do NOT make base active here! screws up GUI stuff, + * if you want it do it at the editor level. */ + const bool object_set_active = false; + + /* First pass on obdata to enable their instantiation by default, then do a second pass on + * objects to clear it for any obdata already in use. */ + if (do_obdata) { + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL) { + continue; + } + const ID_Type idcode = GS(id->name); + if (!OB_DATA_SUPPORT_ID(idcode)) { + continue; + } + + id->tag |= LIB_TAG_DOIT; + } + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + + Object *ob = (Object *)id; + Object *new_ob = (Object *)id->newid; + if (ob->data != NULL) { + ((ID *)(ob->data))->tag &= ~LIB_TAG_DOIT; + } + if (new_ob != NULL && new_ob->data != NULL) { + ((ID *)(new_ob->data))->tag &= ~LIB_TAG_DOIT; + } + } + } + + /* First do collections, then objects, then obdata. */ + + /* NOTE: For collections we only view_layer-instantiate duplicated collections that have + * non-instantiated objects in them. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_GR) { + continue; + } + + /* We do not want to force instantiation of indirectly appended collections. Users can now + * easily instantiate collections (and their objects) as needed by themselves. See T67032. */ + /* We need to check that objects in that collections are already instantiated in a scene. + * Otherwise, it's better to add the collection to the scene's active collection, than to + * instantiate its objects in active scene's collection directly. See T61141. + * + * NOTE: We only check object directly into that collection, not recursively into its + * children. + */ + Collection *collection = (Collection *)id; + bool do_add_collection = false; + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { + Object *ob = coll_ob->ob; + if (!object_in_any_scene(bmain, ob)) { + do_add_collection = true; + break; + } + } + if (do_add_collection) { + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + /* In case user requested instantiation of collections as empties, we do so for the one they + * explicitely selected (originally directly linked IDs). */ + if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 && + (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { + /* BKE_object_add(...) messes with the selection. */ + Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2); + ob->type = OB_EMPTY; + ob->empty_drawsize = U.collection_instance_empty_size; + + BKE_collection_object_add(bmain, active_collection, ob); + + const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; + /* TODO: why is it OK to make this active here but not in other situations? + * See other callers of #object_base_instance_init */ + const bool set_active = set_selected; + wm_append_loose_data_instantiate_object_base_instance_init( + ob, set_selected, set_active, view_layer, v3d); + + /* Assign the collection. */ + ob->instance_collection = collection; + id_us_plus(&collection->id); + ob->transflag |= OB_DUPLICOLLECTION; + copy_v3_v3(ob->loc, scene->cursor.location); + } + else { + /* Add collection as child of active collection. */ + BKE_collection_child_add(bmain, active_collection, collection); + + if ((lapp_data->flag & FILE_AUTOSELECT) != 0) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { + Object *ob = coll_ob->ob; + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base) { + base->flag |= BASE_SELECTED; + BKE_scene_object_base_flag_sync_from_base(base); + } + } + } + } + } + } + + /* NOTE: For objects we only view_layer-instantiate duplicated objects that are not yet used + * anywhere. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + + Object *ob = (Object *)id; + + if (object_in_any_collection(bmain, ob)) { + continue; + } + + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + CLAMP_MIN(ob->id.us, 0); + ob->mode = OB_MODE_OBJECT; + + BKE_collection_object_add(bmain, active_collection, ob); + + wm_append_loose_data_instantiate_object_base_instance_init( + ob, object_set_selected, object_set_active, view_layer, v3d); + } + + if (!do_obdata) { + return; + } + + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL) { + continue; + } + const ID_Type idcode = GS(id->name); + if (!OB_DATA_SUPPORT_ID(idcode)) { + continue; + } + if ((id->tag & LIB_TAG_DOIT) == 0) { + continue; + } + + wm_append_loose_data_instantiate_ensure_active_collection( + lapp_data, bmain, scene, view_layer, &active_collection); + + const int type = BKE_object_obdata_to_type(id); + BLI_assert(type != -1); + Object *ob = BKE_object_add_only_object(bmain, type, id->name + 2); + ob->data = id; + id_us_plus(id); + BKE_object_materials_test(bmain, ob, ob->data); + + BKE_collection_object_add(bmain, active_collection, ob); + + wm_append_loose_data_instantiate_object_base_instance_init( + ob, object_set_selected, object_set_active, view_layer, v3d); + + copy_v3_v3(ob->loc, scene->cursor.location); + } +} + +/* \} */ + +static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) +{ + if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK)) { + return IDWALK_RET_NOP; + } + + WMLinkAppendDataCallBack *data = cb_data->user_data; + ID *id = *cb_data->id_pointer; + + if (id == NULL) { + return IDWALK_RET_NOP; + } + + if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { + return IDWALK_RET_NOP; + } + + WMLinkAppendDataItem *item = BLI_ghash_lookup(data->lapp_data->new_id_to_item, id); + if (item == NULL) { + item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); + item->new_id = id; + /* Since we did not have an item for that ID yet, we now user did not selected it explicitely, + * it was rather linked indirectly. This info is important for instantiation of collections. */ + item->append_tag |= WM_APPEND_TAG_INDIRECT; + BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); + } + + /* NOTE: currently there is no need to do anything else here, but in the future this would be + * the place to add specific per-usage decisions on how to append an ID. */ + + return IDWALK_RET_NOP; +} + +/* Perform append operation, using modern ID usage looper to detect which ID should be kept linked, + * made local, duplicated as local, re-used from local etc. + * + * TODO: Expose somehow this logic to the two other parts of code performing actual append (i.e. + * copy/paste and bpy link/append API). Then we can heavily simplify `BKE_library_make_local()`. */ +static void wm_append_do(WMLinkAppendData *lapp_data, + ReportList *reports, + Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const View3D *v3d) +{ + BLI_assert((lapp_data->flag & FILE_LINK) == 0); + + const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0; + const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0; + + LinkNode *itemlink; + + /* Generate a mapping between newly linked IDs and their items. */ + lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_ghash_insert(lapp_data->new_id_to_item, id, item); + } + + /* Note: Since we append items for IDs not already listed (i.e. implicitely linked indirect + * dependencies), this list will grow and we will process those IDs later, leading to a flatten + * recursive processing of all the linked dependencies. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(item->customdata == NULL); + + /* Clear tag previously used to mark IDs needing post-processing (instanciation of loose + * objects etc.). */ + id->tag &= ~LIB_TAG_DOIT; + + if (item->append_action != WM_APPEND_ACT_UNSET) { + /* Already set, pass. */ + } + if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); + item->append_action = WM_APPEND_ACT_KEEP_LINKED; + } + else if (id->tag & LIB_TAG_PRE_EXISTING) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name); + item->append_action = WM_APPEND_ACT_COPY_LOCAL; + } + else { + /* In future we could search for already existing matching local ID etc. */ + CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name); + item->append_action = WM_APPEND_ACT_MAKE_LOCAL; + } + + /* Only check dependencies if we are not keeping linked data, nor re-using existing local data. + */ + if (do_recursive && + !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + WMLinkAppendDataCallBack cb_data = { + .lapp_data = lapp_data, .item = item, .reports = reports}; + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP); + } + } + + /* Effectively perform required operation on every linked ID. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = item->new_id; + if (id == NULL) { + continue; + } + + switch (item->append_action) { + case WM_APPEND_ACT_COPY_LOCAL: { + BKE_lib_id_make_local( + bmain, id, false, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); + if (id->newid != NULL) { + if (GS(id->newid->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)id->newid); + } + if (set_fakeuser) { + if (!ELEM(GS(id->name), ID_OB, ID_GR)) { + /* Do not set fake user on objects nor collections (instancing). */ + id_fake_user_set(id->newid); + } + } + } + break; + } + case WM_APPEND_ACT_MAKE_LOCAL: + BKE_lib_id_make_local(bmain, + id, + false, + LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | + LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BLI_assert(id->newid == NULL); + if (GS(id->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)id); + } + if (set_fakeuser) { + if (!ELEM(GS(id->name), ID_OB, ID_GR)) { + /* Do not set fake user on objects nor collections (instancing). */ + id_fake_user_set(id); + } + } + break; + case WM_APPEND_ACT_KEEP_LINKED: + /* Nothing to do here. */ + break; + case WM_APPEND_ACT_REUSE_LOCAL: + /* We only need to set `newid` to ID found in previous loop, for proper remapping. */ + ID_NEW_SET(id->newid, item->customdata); + /* Do not set again fake user in case we reuse existing local ID. */ + break; + case WM_APPEND_ACT_UNSET: + CLOG_ERROR( + &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name); + break; + default: + BLI_assert(0); + } + } + + /* Remap IDs as needed. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action == WM_APPEND_ACT_KEEP_LINKED) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + BLI_assert(ID_IS_LINKED(id)); + id = id->newid; + if (id == NULL) { + continue; + } + } + + BLI_assert(!ID_IS_LINKED(id)); + + BKE_libblock_relink_to_newid_new(bmain, id); + } + + /* Instantiate newly created (duplicated) IDs as needed. */ + wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d); + + /* Attempt to deal with object proxies. + * + * NOTE: Copied from `BKE_library_make_local`, but this is not really working (as in, not + * producing any useful result in any known use case), neither here nor in + * `BKE_library_make_local` currently. + * Proxies are end of life anyway, so not worth spending time on this. */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action != WM_APPEND_ACT_COPY_LOCAL) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(ID_IS_LINKED(id)); + + /* Attempt to re-link copied proxy objects. This allows appending of an entire scene + * from another blend file into this one, even when that blend file contains proxified + * armatures that have local references. Since the proxified object needs to be linked + * (not local), this will only work when the "Localize all" checkbox is disabled. + * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ + if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { + Object *ob = (Object *)id; + Object *ob_new = (Object *)id->newid; + bool is_local = false, is_lib = false; + + /* Proxies only work when the proxified object is linked-in from a library. */ + if (!ID_IS_LINKED(ob->proxy)) { + CLOG_WARN(&LOG, + "Proxy object %s will lose its link to %s, because the " + "proxified object is local", + id->newid->name, + ob->proxy->id.name); + continue; + } + + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + + /* We can only switch the proxy'ing to a made-local proxy if it is no longer + * referred to from a library. Not checking for local use; if new local proxy + * was not used locally would be a nasty bug! */ + if (is_local || is_lib) { + CLOG_WARN(&LOG, + "Made-local proxy object %s will lose its link to %s, " + "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)", + id->newid->name, + ob->proxy->id.name, + is_local, + is_lib); + } + else { + /* we can switch the proxy'ing from the linked-in to the made-local proxy. + * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that + * was already allocated by object_make_local() (which called BKE_object_copy). */ + ob_new->proxy = ob->proxy; + ob_new->proxy_group = ob->proxy_group; + ob_new->proxy_from = ob->proxy_from; + ob_new->proxy->proxy_from = ob_new; + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + } + } + } + + BKE_main_id_newptr_and_tag_clear(bmain); +} + static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, @@ -263,6 +871,11 @@ static void wm_link_do(WMLinkAppendData *lapp_data, struct LibraryLink_Params liblink_params; BLO_library_link_params_init_with_context( &liblink_params, bmain, flag, id_tag_extra, scene, view_layer, v3d); + /* In case of append, do not handle instantiation in linking process, but during append phase + * (see #wm_append_loose_data_instantiate ). */ + if ((flag & FILE_LINK) == 0) { + liblink_params.flag &= ~BLO_LIBLINK_NEEDS_ID_TAG_DOIT; + } mainl = BLO_library_link_begin(&bh, libname, &liblink_params); lib = mainl->curlib; @@ -501,28 +1114,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* append, rather than linking */ if (do_append) { - const bool set_fake = RNA_boolean_get(op->ptr, "set_fake"); - const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive"); - - if (use_recursive) { - BKE_library_make_local(bmain, NULL, NULL, true, set_fake); - } - else { - LinkNode *itemlink; - GSet *done_libraries = BLI_gset_new_ex( - BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__, lapp_data->num_libraries); - - for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { - ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id; - - if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) { - BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake); - BLI_gset_insert(done_libraries, new_id->lib); - } - } - - BLI_gset_free(done_libraries, NULL); - } + wm_append_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); } wm_link_append_data_free(lapp_data); @@ -652,20 +1244,20 @@ void WM_OT_append(wmOperatorType *ot) * * \{ */ -static ID *wm_file_link_datablock_ex(Main *bmain, - Scene *scene, - ViewLayer *view_layer, - View3D *v3d, - const char *filepath, - const short id_code, - const char *id_name, - bool clear_pre_existing_flag) +static ID *wm_file_link_append_datablock_ex(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + View3D *v3d, + const char *filepath, + const short id_code, + const char *id_name, + const bool do_append) { /* Tag everything so we can make local only the new datablock. */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* Define working data, with just the one item we want to link. */ - WMLinkAppendData *lapp_data = wm_link_append_data_new(0); + WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0); wm_link_append_data_library_add(lapp_data, filepath); WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); @@ -676,12 +1268,15 @@ static ID *wm_file_link_datablock_ex(Main *bmain, /* Get linked datablock and free working data. */ ID *id = item->new_id; - wm_link_append_data_free(lapp_data); - if (clear_pre_existing_flag) { - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + if (do_append) { + wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d); } + wm_link_append_data_free(lapp_data); + + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + return id; } @@ -693,8 +1288,8 @@ ID *WM_file_link_datablock(Main *bmain, const short id_code, const char *id_name) { - return wm_file_link_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); + return wm_file_link_append_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); } ID *WM_file_append_datablock(Main *bmain, @@ -705,14 +1300,8 @@ ID *WM_file_append_datablock(Main *bmain, const short id_code, const char *id_name) { - ID *id = wm_file_link_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); - - /* Make datablock local. */ - BKE_library_make_local(bmain, NULL, NULL, true, false); - - /* Clear pre existing tag. */ - BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + ID *id = wm_file_link_append_datablock_ex( + bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); return id; } -- cgit v1.2.3 From 0ed089cebda2580a1347d184055b70f0d1f32b76 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 14 Sep 2021 17:59:18 +0200 Subject: LibLink: Enable unittest that was previously failing in append case. Previous commit fixed it. --- tests/python/bl_blendfile_liblink.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index ac71fa85246..992bf6b89d9 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -212,7 +212,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert(len(bpy.data.meshes) == 1) # This one fails currently, for unclear reasons. - # ~ assert(bpy.data.meshes[0].library is not None) + assert(bpy.data.meshes[0].library is not None) assert(bpy.data.meshes[0].users == 1) assert(len(bpy.data.objects) == 1) assert(bpy.data.objects[0].library is None) -- cgit v1.2.3 From 7c7348f78d1b094c4fdc20f2c9dbfe521b68c11e Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Tue, 14 Sep 2021 19:24:36 +0100 Subject: Fix T89027: "factor" field in pose breakdowner not updated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After applying the pose breakdowner, the "factor" slider in the redo panel wasn't set to the correct value This would cause the pose to jump around once you start dragging the slider Reviewed by: Sybren A. Stüvel Differential Revision: https://developer.blender.org/D12187 Ref: D12187 --- source/blender/editors/armature/pose_slide.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index c02644cde39..f23376867af 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -1268,6 +1268,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Perform pose updates - in response to some user action * (e.g. pressing a key or moving the mouse). */ if (do_pose_update) { + RNA_float_set(op->ptr, "factor", ED_slider_factor_get(pso->slider)); + /* Update percentage indicator in header. */ pose_slide_draw_status(C, pso); -- cgit v1.2.3 From e6fa74ffed5ffaf2a7cd550415d8216883fd1961 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 14 Sep 2021 18:13:52 -0500 Subject: Fix: Use after free in spreadsheet attribute column ID A temporary string was created in the attribute_foreach callback and used in a map at a higher scope. When the callback finished, the string went out of scope, was freed, then the elements in the set pointed to freed memory. --- .../editors/space_spreadsheet/spreadsheet_data_source_geometry.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index bd2d89e4f27..78d9f61d8d5 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -54,8 +54,7 @@ void GeometryDataSource::foreach_default_column_ids( return true; } SpreadsheetColumnID column_id; - std::string name = attribute_id.name(); - column_id.name = (char *)name.c_str(); + column_id.name = (char *)attribute_id.name().data(); fn(column_id); return true; }); -- cgit v1.2.3 From 97de4f07a3872365f7924f7c31980eebb79041d0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 10:50:33 +1000 Subject: Cleanup: doxy sections, parameter syntax --- source/blender/blenkernel/intern/lib_id.c | 2 +- source/blender/blenkernel/intern/lib_override.c | 8 ++++---- source/blender/blenkernel/intern/lib_query.c | 6 +++--- source/blender/blenkernel/intern/undo_system.c | 15 +++++++-------- source/blender/blenlib/tests/BLI_color_test.cc | 2 +- source/blender/compositor/intern/COM_WorkScheduler.cc | 10 +++++----- source/blender/compositor/nodes/COM_CryptomatteNode.cc | 14 +++++++++----- source/blender/windowmanager/intern/wm_files_link.c | 2 +- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 66b83272769..4d7d675e364 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1706,7 +1706,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * - * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * \param do_linked_data: if true, also ensure a unique name in case the given \a id is linked * (otherwise, just ensure that it is properly sorted). * * \return true if a new name had to be created. diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 8c1e04838df..3fead8b0f39 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -333,11 +333,11 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, * main. You can add more local IDs to be remapped to use new overriding ones by setting their * LIB_TAG_DOIT tag. * - * \param reference_library the library from which the linked data being overridden come from + * \param reference_library: the library from which the linked data being overridden come from * (i.e. the library of the linked reference ID). * - * \param do_no_main Create the new override data outside of Main database. Used for resyncing of - * linked overrides. + * \param do_no_main: Create the new override data outside of Main database. + * Used for resyncing of linked overrides. * * \return \a true on success, \a false otherwise. */ @@ -901,7 +901,7 @@ static void lib_override_library_create_post_process(Main *bmain, * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we * override, currently. - * \param r_id_root_override if not NULL, the override generated for the given \a id_root. + * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * \return true if override was successfully created. */ bool BKE_lib_override_library_create(Main *bmain, diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 36cbf35b251..2ac92828cec 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -720,9 +720,9 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, * Valid usages here are defined as ref-counting usages, which are not towards embedded or * loop-back data. * - * \param r_num_tagged If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers. - * Number of tagged-as-unused IDs is then set for each type, and as total in - * #INDEX_ID_NULL item. + * \param r_num_tagged: If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers. + * Number of tagged-as-unused IDs is then set for each type, and as total in + * #INDEX_ID_NULL item. */ void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 0ca2b97b4ef..db5184edfd2 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -744,16 +744,15 @@ static UndoStep *undosys_step_iter_first(UndoStep *us_reference, const eUndoStep /** * Undo/Redo until the given `us_target` step becomes the active (currently loaded) one. * - * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the - * active step. + * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` + * will become the active step. * - * \note In case `use_skip` is true, the final target will always be **beyond** the given one (if - * the given one has to be skipped). + * \note In case `use_skip` is true, the final target will always be **beyond** the given one + * (if the given one has to be skipped). * - * \param us_reference If NULL, will be set to current active step in the undo stack. Otherwise, it - * is assumed to match the current state, and will be used as basis for the - * undo/redo process (i.e. all steps in-between `us_reference` and `us_target` - * will be processed). + * \param us_reference: If NULL, will be set to current active step in the undo stack. Otherwise, + * it is assumed to match the current state, and will be used as basis for the undo/redo process + * (i.e. all steps in-between `us_reference` and `us_target` will be processed). */ bool BKE_undosys_step_load_data_ex(UndoStack *ustack, bContext *C, diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc index 14796e6bf71..a91c743b133 100644 --- a/source/blender/blenlib/tests/BLI_color_test.cc +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -128,6 +128,6 @@ TEST(color, SceneLinearByteDecoding) EXPECT_NEAR(0.5f, decoded.a, 0.01f); } -/* \} */ +/** \} */ } // namespace blender::tests diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index 8e49bf34b51..a08f9dd284c 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -298,7 +298,7 @@ static void opencl_deinitialize() g_work_scheduler.opencl.initialized = false; } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Single threaded Scheduling @@ -310,7 +310,7 @@ static void threading_model_single_thread_execute(WorkPackage *package) device.execute(package); } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Queue Scheduling @@ -388,7 +388,7 @@ static void threading_model_queue_deinitialize() } } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Task Scheduling @@ -426,7 +426,7 @@ static void threading_model_task_stop() BLI_thread_local_delete(g_thread_device); } -/* \} */ +/** \} */ /* -------------------------------------------------------------------- */ /** \name Public API @@ -587,6 +587,6 @@ int WorkScheduler::current_thread_id() return device->thread_id(); } -/* \} */ +/** \} */ } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index 5835f051ce3..c04d98d6a2b 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -33,7 +33,8 @@ namespace blender::compositor { -/** \name Cryptomatte base +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Base * \{ */ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, @@ -73,10 +74,12 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(output_pick_socket, extract_pick_operation->getOutputSocket(0)); } -/* \} */ +/** \} */ +/* -------------------------------------------------------------------- */ /** \name Cryptomatte V2 * \{ */ + static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; @@ -247,9 +250,10 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation( return operation; } -/* \} */ +/** \} */ -/** \name Cryptomatte legacy +/* -------------------------------------------------------------------- */ +/** \name Cryptomatte Legacy * \{ */ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( @@ -273,6 +277,6 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( return operation; } -/* \} */ +/** \} */ } // namespace blender::compositor diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 4affabb6da2..512320d3850 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -565,7 +565,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, } } -/* \} */ +/** \} */ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) { -- cgit v1.2.3 From e1714ce8c90974f563d5071e4a67a249e7626fb3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 10:50:36 +1000 Subject: Cleanup: spelling --- source/blender/editors/space_outliner/outliner_draw.c | 2 +- source/blender/windowmanager/WM_types.h | 2 +- source/blender/windowmanager/intern/wm_files_link.c | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index a094dfb0834..c06a1010168 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2633,7 +2633,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_FILE_TEXT; } else { - /* Helps distinguish text-based formats like the filebrowser does. */ + /* Helps distinguish text-based formats like the file-browser does. */ data.icon = ED_file_extension_icon(text->filepath); } break; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 8a1ff67b37c..c1730957432 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -925,7 +925,7 @@ typedef struct wmDragID { } wmDragID; typedef struct wmDragAsset { - /* Note: Can't store the AssetHandle here, since the FileDirEntry it wraps may be freed while + /* NOTE: Can't store the #AssetHandle here, since the #FileDirEntry it wraps may be freed while * dragging. So store necessary data here directly. */ char name[64]; /* MAX_NAME */ diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 512320d3850..2416f5b50b3 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -460,7 +460,7 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, lapp_data, bmain, scene, view_layer, &active_collection); /* In case user requested instantiation of collections as empties, we do so for the one they - * explicitely selected (originally directly linked IDs). */ + * explicitly selected (originally directly linked IDs). */ if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 && (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { /* BKE_object_add(...) messes with the selection. */ @@ -588,7 +588,7 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) if (item == NULL) { item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); item->new_id = id; - /* Since we did not have an item for that ID yet, we now user did not selected it explicitely, + /* Since we did not have an item for that ID yet, we now user did not selected it explicitly, * it was rather linked indirectly. This info is important for instantiation of collections. */ item->append_tag |= WM_APPEND_TAG_INDIRECT; BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); @@ -603,8 +603,9 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) /* Perform append operation, using modern ID usage looper to detect which ID should be kept linked, * made local, duplicated as local, re-used from local etc. * - * TODO: Expose somehow this logic to the two other parts of code performing actual append (i.e. - * copy/paste and bpy link/append API). Then we can heavily simplify `BKE_library_make_local()`. */ + * TODO: Expose somehow this logic to the two other parts of code performing actual append + * (i.e. copy/paste and `bpy` link/append API). + * Then we can heavily simplify #BKE_library_make_local(). */ static void wm_append_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, @@ -630,7 +631,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_ghash_insert(lapp_data->new_id_to_item, id, item); } - /* Note: Since we append items for IDs not already listed (i.e. implicitely linked indirect + /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect * dependencies), this list will grow and we will process those IDs later, leading to a flatten * recursive processing of all the linked dependencies. */ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { @@ -641,7 +642,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } BLI_assert(item->customdata == NULL); - /* Clear tag previously used to mark IDs needing post-processing (instanciation of loose + /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose * objects etc.). */ id->tag &= ~LIB_TAG_DOIT; -- cgit v1.2.3 From 56f8d7c7059728bad0fe087c291e4829398eab16 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 11:13:10 +1000 Subject: Fix T89241: Scale to fit overflows into a second line --- source/blender/blenkernel/intern/font.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index c1765967238..b6e374d158f 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -719,6 +719,9 @@ typedef struct VFontToCurveIter { * * Currently only disabled when scale-to-fit is enabled, * so floating-point error doesn't cause unexpected wrapping, see T89241. + * + * \note This should only be set once, in the #VFONT_TO_CURVE_INIT pass + * otherwise iterations wont behave predictably, see T89241. */ bool word_wrap; int status; @@ -1640,7 +1643,6 @@ static bool vfont_to_curve(Object *ob, else { iter_data->scale_to_fit = iter_data->bisect.min; iter_data->status = VFONT_TO_CURVE_SCALE_ONCE; - iter_data->word_wrap = false; } } } -- cgit v1.2.3 From 94dd30208e3f54ac2a6863b858fdb81dd840feca Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 14 Sep 2021 22:57:46 -0400 Subject: UI: Addition Changes to Freestyle Properties - Material Properties: Use split column layout - Remove the redundent term 'Options' - Remove the redundent term 'Freesttle' --- release/scripts/startup/bl_ui/properties_freestyle.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index 5ca44bb6555..3c765c10154 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -168,8 +168,8 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel): col.prop(freestyle, "as_render_pass", text="As Render Pass") -class VIEWLAYER_PT_freestyle_edge_detection_options(ViewLayerFreestyleButtonsPanel, Panel): - bl_label = "Edge Detection Options" +class VIEWLAYER_PT_freestyle_edge_detection(ViewLayerFreestyleButtonsPanel, Panel): + bl_label = "Edge Detection" bl_parent_id = "VIEWLAYER_PT_freestyle" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @@ -435,7 +435,7 @@ class VIEWLAYER_PT_freestyle_lineset_collection(ViewLayerFreestyleLineStyle, Pan lineset = freestyle.linesets.active layout.active = lineset.select_by_collection - layout.row().prop(lineset, "collection", text="Freestyle Lineset Collection") + layout.row().prop(lineset, "collection", text="Line Set Collection") layout.row().prop(lineset, "collection_negation", expand=True, text="Negation") @@ -1258,12 +1258,14 @@ class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False mat = context.material - row = layout.row() - row.prop(mat, "line_color", text="") - row.prop(mat, "line_priority", text="Priority") + col = layout.column() + col.prop(mat, "line_color") + col.prop(mat, "line_priority", text="Priority") classes = ( @@ -1271,7 +1273,7 @@ classes = ( VIEWLAYER_UL_linesets, RENDER_MT_lineset_context_menu, VIEWLAYER_PT_freestyle, - VIEWLAYER_PT_freestyle_edge_detection_options, + VIEWLAYER_PT_freestyle_edge_detection, VIEWLAYER_PT_freestyle_style_modules, VIEWLAYER_PT_freestyle_lineset, VIEWLAYER_PT_freestyle_lineset_visibilty, -- cgit v1.2.3 From 202dc3631e3f27c53c48d76e818170d204995794 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Tue, 14 Sep 2021 23:16:38 -0400 Subject: PyDocs: Update theme to latest version --- doc/python_api/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python_api/requirements.txt b/doc/python_api/requirements.txt index b5a9d15bf7b..51440046430 100644 --- a/doc/python_api/requirements.txt +++ b/doc/python_api/requirements.txt @@ -10,4 +10,4 @@ requests==2.26.0 # Only needed to match the theme used for the official documentation. # Without this theme, the default theme will be used. -sphinx_rtd_theme==1.0.0rc1 +sphinx_rtd_theme==1.0.0 -- cgit v1.2.3 From c1cf66bff3c0753512a2d1f2f8c03430bdd1f045 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 15 Sep 2021 14:03:18 +0800 Subject: LineArt: Automatic crease with flat/smooth faces. This allows crease lines to be automatically hidden on smooth surfaces, also provided options for: - Showing crease on marked sharp edges. - Force crease detection on smooth surfaces. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: http://developer.blender.org/D12051 --- source/blender/blenloader/intern/versioning_300.c | 15 ++++++++ .../gpencil_modifiers/intern/MOD_gpencillineart.c | 2 ++ .../gpencil_modifiers/intern/lineart/MOD_lineart.h | 3 ++ .../gpencil_modifiers/intern/lineart/lineart_cpu.c | 42 +++++++++++++++------- .../makesdna/DNA_gpencil_modifier_defaults.h | 2 +- source/blender/makesdna/DNA_lineart_types.h | 2 ++ .../blender/makesrna/intern/rna_gpencil_modifier.c | 12 +++++++ 7 files changed, 65 insertions(+), 13 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 6b8f011e9fc..4eba9f5f42e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -37,6 +37,8 @@ #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_genfile.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_lineart_types.h" #include "DNA_listBase.h" #include "DNA_material_types.h" #include "DNA_modifier_types.h" @@ -1225,6 +1227,19 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) version_geometry_nodes_change_legacy_names(ntree); } } + if (!DNA_struct_elem_find( + fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->type == OB_GPENCIL) { + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + lmd->calculation_flags |= LRT_USE_CREASE_ON_SMOOTH_SURFACES; + } + } + } + } + } } /** diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 73ca4b9c529..01488a8b2de 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -390,6 +390,8 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_edge_overlap", 0, IFACE_("Overlapping Edges As Contour"), ICON_NONE); uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); + uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 4b71011b99a..134d9707ade 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -304,6 +304,9 @@ typedef struct LineartRenderBuffer { bool filter_face_mark_invert; bool filter_face_mark_boundaries; + bool force_crease; + bool sharp_as_crease; + /* Keep an copy of these data so when line art is running it's self-contained. */ bool cam_is_persp; float cam_obmat[4][4]; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index c71cde8ec43..725cc0741f0 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1453,7 +1453,7 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, LineartTriangle *rt_array, LineartVert *rv_array, float crease_threshold, - bool no_crease, + bool use_auto_smooth, bool use_freestyle_edge, bool use_freestyle_face, BMesh *bm_if_freestyle) @@ -1533,10 +1533,20 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } - if (rb->use_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { - if (!no_crease) { + if (rb->use_crease) { + if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; } + else { + bool do_crease = true; + if (!rb->force_crease && !use_auto_smooth && + (BM_elem_flag_test(ll->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(lr->f, BM_ELEM_SMOOTH))) { + do_crease = false; + } + if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < crease_threshold)) { + edge_flag_result |= LRT_EDGE_FLAG_CREASE; + } + } } if (rb->use_material && (ll->f->mat_nr != lr->f->mat_nr)) { edge_flag_result |= LRT_EDGE_FLAG_MATERIAL; @@ -1746,9 +1756,14 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu eln->object_ref = orig_ob; obi->v_eln = eln; + bool use_auto_smooth = false; if (orig_ob->lineart.flags & OBJECT_LRT_OWN_CREASE) { use_crease = cosf(M_PI - orig_ob->lineart.crease_threshold); } + if (obi->original_me->flag & ME_AUTOSMOOTH) { + use_crease = cosf(obi->original_me->smoothresh); + use_auto_smooth = true; + } else { use_crease = rb->crease_threshold; } @@ -1832,15 +1847,15 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu e = BM_edge_at_index(bm, i); /* Because e->head.hflag is char, so line type flags should not exceed positive 7 bits. */ - char eflag = lineart_identify_feature_line(rb, - e, - ort, - orv, - use_crease, - orig_ob->type == OB_FONT, - can_find_freestyle_edge, - can_find_freestyle_face, - bm); + uint16_t eflag = lineart_identify_feature_line(rb, + e, + ort, + orv, + use_crease, + use_auto_smooth, + can_find_freestyle_edge, + can_find_freestyle_face, + bm); if (eflag) { /* Only allocate for feature lines (instead of all lines) to save memory. * If allow duplicated edges, one edge gets added multiple times if it has multiple types. */ @@ -3057,6 +3072,9 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->allow_duplicated_types = (lmd->calculation_flags & LRT_ALLOW_OVERLAP_EDGE_TYPES) != 0; + rb->force_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SMOOTH_SURFACES) != 0; + rb->sharp_as_crease = (lmd->calculation_flags & LRT_USE_CREASE_ON_SHARP_EDGES) != 0; + int16_t edge_types = lmd->edge_types_override; rb->use_contour = (edge_types & LRT_EDGE_FLAG_CONTOUR) != 0; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 81e9abc4916..8ab15b97537 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -304,7 +304,7 @@ .opacity = 1.0f, \ .flags = LRT_GPENCIL_MATCH_OUTPUT_VGROUP, \ .crease_threshold = DEG2RAD(140.0f), \ - .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \ + .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ .overscan = 0.1f,\ diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index cdb09c3af50..bdc9bcb6980 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -47,6 +47,8 @@ typedef enum eLineartMainFlags { LRT_CHAIN_LOOSE_EDGES = (1 << 12), LRT_CHAIN_GEOMETRY_SPACE = (1 << 13), LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), + LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), + LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 55fe74cab4f..e125149dad2 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -3188,6 +3188,18 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Masks", "Mask bits to match from Collection Line Art settings"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_crease_on_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "calculation_flags", LRT_USE_CREASE_ON_SMOOTH_SURFACES); + RNA_def_property_ui_text( + prop, "Crease On Smooth Surfaces", "Allow crease edges to show inside smooth surfaces"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_crease_on_sharp", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CREASE_ON_SHARP_EDGES); + RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } -- cgit v1.2.3 From a2c5c2b4068d12e26a8c9d640a389b410c7507f3 Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 15 Sep 2021 14:24:28 +0800 Subject: GPencil: Dot dash modifier. Create dot-dash effect for grease pencil strokes. User can manually edit the length, gap and styles for each segment of dashed lines. The values in each segment can all be key-framed to make animations. Reviewed By: Hans Goudey (HooglyBoogly), Antonio Vazquez (antoniov) Differential Revision: http://developer.blender.org/D11876 --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- .../blender/blenkernel/intern/gpencil_modifier.c | 9 + source/blender/editors/datafiles/CMakeLists.txt | 1 + source/blender/editors/object/CMakeLists.txt | 5 + .../editors/object/object_gpencil_modifier.c | 238 +++++++++++++ source/blender/editors/object/object_intern.h | 4 + source/blender/editors/object/object_ops.c | 4 + source/blender/gpencil_modifiers/CMakeLists.txt | 1 + .../gpencil_modifiers/MOD_gpencil_modifiertypes.h | 1 + .../gpencil_modifiers/intern/MOD_gpencil_util.c | 1 + .../gpencil_modifiers/intern/MOD_gpencildash.c | 387 +++++++++++++++++++++ .../makesdna/DNA_gpencil_modifier_defaults.h | 18 + .../blender/makesdna/DNA_gpencil_modifier_types.h | 34 ++ source/blender/makesdna/intern/dna_defaults.c | 4 + source/blender/makesrna/RNA_access.h | 2 + .../blender/makesrna/intern/rna_gpencil_modifier.c | 200 ++++++++++- source/tools | 2 +- 19 files changed, 909 insertions(+), 8 deletions(-) create mode 100644 source/blender/gpencil_modifiers/intern/MOD_gpencildash.c diff --git a/release/datafiles/locale b/release/datafiles/locale index 8a05b618f03..62e82958a76 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 8a05b618f031582c006c6f62b9e60619ab3eef8b +Subproject commit 62e82958a760dad775d9b3387d7fb535fd6de4c6 diff --git a/release/scripts/addons b/release/scripts/addons index 67f1fbca148..4475cbd11a6 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230 +Subproject commit 4475cbd11a636382d57571e0f5dfeff1f90bd6b7 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index ef6ef414d22..788441f2930 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit ef6ef414d22c2578fad99327743b925ab640a99c +Subproject commit 788441f2930465bbfba8f0797b12dcef1d46694d diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index a30376b9bad..b120c901499 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -938,6 +938,11 @@ void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase) BKE_curvemapping_blend_write(writer, gpmd->curve_intensity); } } + else if (md->type == eGpencilModifierType_Dash) { + DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md; + BLO_write_struct_array( + writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments); + } } } @@ -1017,6 +1022,10 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb) BKE_curvemapping_init(gpmd->curve_intensity); } } + else if (md->type == eGpencilModifierType_Dash) { + DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md; + BLO_read_data_address(reader, &gpmd->segments); + } } } diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 669ceb37328..702fd2e375a 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -336,6 +336,7 @@ set(ICON_NAMES lightprobe_cubemap lightprobe_planar lightprobe_grid + mod_dash color_red color_green color_blue diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 18f2b58eb65..040b5cd5066 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -37,6 +37,9 @@ set(INC ../../../../intern/clog ../../../../intern/glew-mx ../../../../intern/guardedalloc + + # dna_type_offsets.h in BLO_read_write.h + ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern ) set(SRC @@ -93,3 +96,5 @@ if(WITH_EXPERIMENTAL_FEATURES) endif() blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +add_dependencies(bf_editor_object bf_dna) diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 3995728c428..e3c2932e17a 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -28,6 +28,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" @@ -35,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -55,6 +57,8 @@ #include "ED_object.h" #include "ED_screen.h" +#include "BLT_translation.h" + #include "UI_interface.h" #include "WM_api.h" @@ -939,3 +943,237 @@ void OBJECT_OT_gpencil_modifier_copy_to_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); } + +/************************* Dash Modifier *******************************/ + +static bool dash_segment_poll(bContext *C) +{ + return gpencil_edit_modifier_poll_generic(C, &RNA_DashGpencilModifierData, 0, false); +} + +static bool dash_segment_name_exists_fn(void *arg, const char *name) +{ + const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; + for (int i = 0; i < dmd->segments_len; i++) { + if (STREQ(dmd->segments[i].name, name)) { + return true; + } + } + return false; +} + +static int dash_segment_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + const int new_active_index = dmd->segment_active_index + 1; + DashGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + dmd->segments_len + 1, sizeof(DashGpencilModifierSegment), __func__); + + if (dmd->segments_len != 0) { + /* Copy the segments before the new segment. */ + memcpy(new_segments, dmd->segments, sizeof(DashGpencilModifierSegment) * new_active_index); + /* Copy the segments after the new segment. */ + memcpy(new_segments + new_active_index + 1, + dmd->segments + new_active_index, + sizeof(DashGpencilModifierSegment) * (dmd->segments_len - new_active_index)); + } + + /* Create the new segment. */ + DashGpencilModifierSegment *ds = &new_segments[new_active_index]; + memcpy( + ds, DNA_struct_default_get(DashGpencilModifierSegment), sizeof(DashGpencilModifierSegment)); + BLI_uniquename_cb( + dash_segment_name_exists_fn, dmd, DATA_("Segment"), '.', ds->name, sizeof(ds->name)); + ds->dmd = dmd; + + MEM_SAFE_FREE(dmd->segments); + dmd->segments = new_segments; + dmd->segments_len++; + dmd->segment_active_index++; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_add_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Segment"; + ot->description = "Add a segment to the dash modifier"; + ot->idname = "GPENCIL_OT_segment_add"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_add_invoke; + ot->exec = dash_segment_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +static int dash_segment_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + if (dmd->segment_active_index < 0 || dmd->segment_active_index >= dmd->segments_len) { + return OPERATOR_CANCELLED; + } + + if (dmd->segments_len == 1) { + MEM_SAFE_FREE(dmd->segments); + dmd->segment_active_index = -1; + } + else { + DashGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + dmd->segments_len, sizeof(DashGpencilModifierSegment), __func__); + + /* Copy the segments before the deleted segment. */ + memcpy(new_segments, + dmd->segments, + sizeof(DashGpencilModifierSegment) * dmd->segment_active_index); + + /* Copy the segments after the deleted segment. */ + memcpy(new_segments + dmd->segment_active_index, + dmd->segments + dmd->segment_active_index + 1, + sizeof(DashGpencilModifierSegment) * + (dmd->segments_len - dmd->segment_active_index - 1)); + + MEM_freeN(dmd->segments); + dmd->segments = new_segments; + dmd->segment_active_index = MAX2(dmd->segment_active_index - 1, 0); + } + + dmd->segments_len--; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_remove_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Dash Segment"; + ot->description = "Remove the active segment from the dash modifier"; + ot->idname = "GPENCIL_OT_segment_remove"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_remove_invoke; + ot->exec = dash_segment_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + RNA_def_int( + ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); +} + +enum { + GP_SEGEMENT_MOVE_UP = -1, + GP_SEGEMENT_MOVE_DOWN = 1, +}; + +static int dash_segment_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Dash); + + if (dmd->segments_len < 2) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "type"); + if (direction == GP_SEGEMENT_MOVE_UP) { + if (dmd->segment_active_index == 0) { + return OPERATOR_CANCELLED; + } + + SWAP(DashGpencilModifierSegment, + dmd->segments[dmd->segment_active_index], + dmd->segments[dmd->segment_active_index - 1]); + + dmd->segment_active_index--; + } + else if (direction == GP_SEGEMENT_MOVE_DOWN) { + if (dmd->segment_active_index == dmd->segments_len - 1) { + return OPERATOR_CANCELLED; + } + + SWAP(DashGpencilModifierSegment, + dmd->segments[dmd->segment_active_index], + dmd->segments[dmd->segment_active_index + 1]); + + dmd->segment_active_index++; + } + else { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int dash_segment_move_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return dash_segment_move_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_segment_move(wmOperatorType *ot) +{ + static const EnumPropertyItem segment_move[] = { + {GP_SEGEMENT_MOVE_UP, "UP", 0, "Up", ""}, + {GP_SEGEMENT_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Dash Segment"; + ot->description = "Move the active dash segment up or down"; + ot->idname = "GPENCIL_OT_segment_move"; + + /* api callbacks */ + ot->poll = dash_segment_poll; + ot->invoke = dash_segment_move_invoke; + ot->exec = dash_segment_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", ""); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 10e016738d0..b2d3216b101 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -202,6 +202,10 @@ void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy_to_selected(struct wmOperatorType *ot); +void GPENCIL_OT_segment_add(struct wmOperatorType *ot); +void GPENCIL_OT_segment_remove(struct wmOperatorType *ot); +void GPENCIL_OT_segment_move(struct wmOperatorType *ot); + /* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); void OBJECT_OT_shaderfx_copy(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index c1928cf7f8a..4b8431be530 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -156,6 +156,10 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy); WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected); + WM_operatortype_append(GPENCIL_OT_segment_add); + WM_operatortype_append(GPENCIL_OT_segment_remove); + WM_operatortype_append(GPENCIL_OT_segment_move); + /* grease pencil line art */ WM_operatortypes_lineart(); diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index ec965c9a29f..adf68e534bb 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC intern/MOD_gpencilarray.c intern/MOD_gpencilbuild.c intern/MOD_gpencilcolor.c + intern/MOD_gpencildash.c intern/MOD_gpencilhook.c intern/MOD_gpencillattice.c intern/MOD_gpencillength.c diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index 18310bd5dff..043186155b7 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -46,6 +46,7 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply; extern GpencilModifierTypeInfo modifierType_Gpencil_Texture; extern GpencilModifierTypeInfo modifierType_Gpencil_Weight; extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart; +extern GpencilModifierTypeInfo modifierType_Gpencil_Dash; /* MOD_gpencil_util.c */ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 6409c86b6e3..5eb1eeab780 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -65,6 +65,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Texture); INIT_GP_TYPE(Weight); INIT_GP_TYPE(Lineart); + INIT_GP_TYPE(Dash); #undef INIT_GP_TYPE } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c new file mode 100644 index 00000000000..ba33edd6a94 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -0,0 +1,387 @@ +/* + * 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) 2021, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup modifiers + */ + +#include +#include + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "MEM_guardedalloc.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "BLT_translation.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" + +static void initData(GpencilModifierData *md) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(dmd, modifier)); + + MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(DashGpencilModifierData), modifier); + + DashGpencilModifierSegment *ds = DNA_struct_default_alloc(DashGpencilModifierSegment); + ds->dmd = dmd; + BLI_strncpy(ds->name, DATA_("Segment"), sizeof(ds->name)); + + dmd->segments = ds; +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)target; + const DashGpencilModifierData *dmd_src = (const DashGpencilModifierData *)md; + + BKE_gpencil_modifier_copydata_generic(md, target); + + dmd->segments = MEM_dupallocN(dmd_src->segments); +} + +static void freeData(GpencilModifierData *md) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)md; + + MEM_SAFE_FREE(dmd->segments); +} + +/** + * Gap==0 means to start the next segment at the immediate next point, which will leave a visual + * gap of "1 point". This makes the algorithm give the same visual appearance as displayed on the + * UI and also simplifies the check for "no-length" situation where SEG==0 (which will not produce + * any effective dash). + */ +static int real_gap(const DashGpencilModifierSegment *ds) +{ + return ds->gap - 1; +} + +static bool stroke_dash(const bGPDstroke *gps, + const DashGpencilModifierData *dmd, + ListBase *r_strokes) +{ + int new_stroke_offset = 0; + int trim_start = 0; + + for (int i = 0; i < dmd->segments_len; i++) { + if (dmd->segments[i].dash + real_gap(&dmd->segments[i]) < 1) { + BLI_assert_unreachable(); + /* This means there's a part that doesn't have any length, can't do dot-dash. */ + return false; + } + } + + const DashGpencilModifierSegment *const first_segment = &dmd->segments[0]; + const DashGpencilModifierSegment *const last_segment = &dmd->segments[dmd->segments_len - 1]; + const DashGpencilModifierSegment *ds = first_segment; + + /* Determine starting configuration using offset. */ + int offset_trim = dmd->dash_offset; + while (offset_trim < 0) { + ds = (ds == first_segment) ? last_segment : ds - 1; + offset_trim += ds->dash + real_gap(ds); + } + + /* This segment is completely removed from view by the index offset, ignore it. */ + while (ds->dash + real_gap(ds) < offset_trim) { + offset_trim -= ds->dash + real_gap(ds); + ds = (ds == last_segment) ? first_segment : ds + 1; + } + + /* This segment is partially visible at the beginning of the stroke. */ + if (ds->dash > offset_trim) { + trim_start = offset_trim; + } + else { + /* This segment is not visible but the gap immediately after this segment is partially visible, + * use next segment's dash. */ + new_stroke_offset += ds->dash + real_gap(ds) - offset_trim; + ds = (ds == last_segment) ? first_segment : ds + 1; + } + + while (new_stroke_offset < gps->totpoints - 1) { + const int seg = ds->dash - trim_start; + if (!(seg || real_gap(ds))) { + ds = (ds == last_segment) ? first_segment : ds + 1; + continue; + } + + const int size = MIN2(gps->totpoints - new_stroke_offset, seg); + if (size == 0) { + continue; + } + + bGPDstroke *stroke = BKE_gpencil_stroke_new( + ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness); + + for (int is = 0; is < size; is++) { + bGPDspoint *p = &gps->points[new_stroke_offset + is]; + stroke->points[is].x = p->x; + stroke->points[is].y = p->y; + stroke->points[is].z = p->z; + stroke->points[is].pressure = p->pressure * ds->radius; + stroke->points[is].strength = p->strength * ds->opacity; + } + BLI_addtail(r_strokes, stroke); + + if (gps->dvert) { + BKE_gpencil_dvert_ensure(stroke); + for (int di = 0; di < stroke->totpoints; di++) { + MDeformVert *dv = &gps->dvert[new_stroke_offset + di]; + if (dv && dv->totweight && dv->dw) { + MDeformWeight *dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + stroke->dvert[di].dw = dw; + stroke->dvert[di].totweight = dv->totweight; + stroke->dvert[di].flag = dv->flag; + } + } + } + + new_stroke_offset += seg + real_gap(ds); + ds = (ds == last_segment) ? first_segment : ds + 1; + trim_start = 0; + } + + return true; +} + +static void apply_dash_for_frame( + Object *ob, bGPDlayer *gpl, bGPdata *gpd, bGPDframe *gpf, DashGpencilModifierData *dmd) +{ + if (dmd->segments_len == 0) { + return; + } + + ListBase result = {NULL, NULL}; + + LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + if (is_stroke_affected_by_modifier(ob, + dmd->layername, + dmd->material, + dmd->pass_index, + dmd->layer_pass, + 1, + gpl, + gps, + dmd->flag & GP_LENGTH_INVERT_LAYER, + dmd->flag & GP_LENGTH_INVERT_PASS, + dmd->flag & GP_LENGTH_INVERT_LAYERPASS, + dmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + stroke_dash(gps, dmd, &result); + BLI_remlink(&gpf->strokes, gps); + BKE_gpencil_free_stroke(gps); + } + } + bGPDstroke *gps_dash; + while ((gps_dash = BLI_pophead(&result))) { + BLI_addtail(&gpf->strokes, gps_dash); + BKE_gpencil_stroke_geometry_update(gpd, gps_dash); + } +} + +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *UNUSED(depsgraph), + GpencilModifierData *md, + Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); + } + } +} + +/* -------------------------------- */ + +/* Generic "generateStrokes" callback */ +static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + BKE_gpencil_frame_active_set(depsgraph, gpd); + bGPDframe *gpf = gpl->actframe; + if (gpf == NULL) { + return; + } + apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); + } +} + +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + DashGpencilModifierData *mmd = (DashGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} + +static void segment_list_item(struct uiList *UNUSED(ui_list), + struct bContext *UNUSED(C), + struct uiLayout *layout, + struct PointerRNA *UNUSED(idataptr), + struct PointerRNA *itemptr, + int UNUSED(icon), + struct PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE); +} + +static void panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "dash_offset", 0, NULL, ICON_NONE); + + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetPropSep(row, false); + + uiTemplateList(row, + (bContext *)C, + "MOD_UL_dash_segment", + "", + ptr, + "segments", + ptr, + "segment_active_index", + NULL, + 3, + 10, + 0, + 1, + UI_TEMPLATE_LIST_FLAG_NONE); + + uiLayout *col = uiLayoutColumn(row, false); + uiLayoutSetContextPointer(col, "modifier", ptr); + + uiLayout *sub = uiLayoutColumn(col, true); + uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_segment_add"); + uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_segment_remove"); + uiItemS(col); + sub = uiLayoutColumn(col, true); + uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_segment_move", "type", "UP"); + uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_segment_move", "type", "DOWN"); + + DashGpencilModifierData *dmd = ptr->data; + + if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_len) { + PointerRNA ds_ptr; + RNA_pointer_create(ptr->owner_id, + &RNA_DashGpencilModifierSegment, + &dmd->segments[dmd->segment_active_index], + &ds_ptr); + + sub = uiLayoutColumn(layout, true); + uiItemR(sub, &ds_ptr, "dash", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "gap", 0, NULL, ICON_NONE); + + sub = uiLayoutColumn(layout, false); + uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE); + } + + gpencil_modifier_panel_end(layout, ptr); +} + +static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + gpencil_modifier_masking_panel_draw(panel, true, false); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = gpencil_modifier_panel_register( + region_type, eGpencilModifierType_Dash, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); + + uiListType *list_type = MEM_callocN(sizeof(uiListType), "dash modifier segment uilist"); + strcpy(list_type->idname, "MOD_UL_dash_segment"); + list_type->draw_item = segment_list_item; + WM_uilisttype_add(list_type); +} + +GpencilModifierTypeInfo modifierType_Gpencil_Dash = { + /* name */ "Dot Dash", + /* structName */ "DashGpencilModifierData", + /* structSize */ sizeof(DashGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, + + /* copyData */ copyData, + + /* deformStroke */ NULL, + /* generateStrokes */ generateStrokes, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ freeData, + /* isDisabled */ NULL, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, + /* panelRegister */ panelRegister, +}; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 8ab15b97537..450527c7443 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -319,5 +319,23 @@ .material = NULL,\ } +#define _DNA_DEFAULT_DashGpencilModifierData \ + { \ + .dash_offset = 0, \ + .segments = NULL, \ + .segments_len = 1, \ + .segment_active_index = 0, \ + } + +#define _DNA_DEFAULT_DashGpencilModifierSegment \ + { \ + .name = "", \ + .dash = 2, \ + .gap = 1, \ + .radius = 1.0f, \ + .opacity = 1.0f, \ + .mat_nr = -1, \ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index c91afa58cb1..d3429329ef6 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -56,6 +56,7 @@ typedef enum GpencilModifierType { eGpencilModifierType_Lineart = 19, eGpencilModifierType_Length = 20, eGpencilModifierType_Weight = 21, + eGpencilModifierType_Dash = 22, /* Keep last. */ NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -507,6 +508,39 @@ typedef enum eLengthGpencil_Type { GP_LENGTH_ABSOLUTE = 1, } eLengthGpencil_Type; +typedef struct DashGpencilModifierSegment { + char name[64]; + /* For path reference. */ + struct DashGpencilModifierData *dmd; + int dash; + int gap; + float radius; + float opacity; + int mat_nr; + int _pad; +} DashGpencilModifierSegment; + +typedef struct DashGpencilModifierData { + GpencilModifierData modifier; + /** Material for filtering. */ + struct Material *material; + /** Layer name. */ + char layername[64]; + /** Custom index for passes. */ + int pass_index; + /** Flags. */ + int flag; + /** Custom index for passes. */ + int layer_pass; + + int dash_offset; + + DashGpencilModifierSegment *segments; + int segments_len; + int segment_active_index; + +} DashGpencilModifierData; + typedef struct MirrorGpencilModifierData { GpencilModifierData modifier; struct Object *object; diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 3570f5a6a6f..4cb8610f6ac 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -321,6 +321,8 @@ SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(WeightGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierSegment); #undef SDNA_DEFAULT_DECL_STRUCT @@ -549,6 +551,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(WeightGpencilModifierData), SDNA_DEFAULT_DECL(LineartGpencilModifierData), SDNA_DEFAULT_DECL(LengthGpencilModifierData), + SDNA_DEFAULT_DECL(DashGpencilModifierData), + SDNA_DEFAULT_DECL(DashGpencilModifierSegment), }; #undef SDNA_DEFAULT_DECL #undef SDNA_DEFAULT_DECL_EX diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f5ebdbb569e..ce53e3390e1 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -222,6 +222,8 @@ extern StructRNA RNA_CurvePoint; extern StructRNA RNA_CurveProfile; extern StructRNA RNA_CurveProfilePoint; extern StructRNA RNA_DampedTrackConstraint; +extern StructRNA RNA_DashGpencilModifierData; +extern StructRNA RNA_DashGpencilModifierSegment; extern StructRNA RNA_DataTransferModifier; extern StructRNA RNA_DecimateModifier; extern StructRNA RNA_Depsgraph; diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index e125149dad2..4fa33424994 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -35,6 +35,7 @@ #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" @@ -68,6 +69,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_BUILD, "Build", "Create duplication of strokes"}, + {eGpencilModifierType_Dash, + "GP_DASH", + ICON_MOD_DASH, + "Dot Dash", + "Generate dot-dash styled strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -268,6 +274,8 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_TextureGpencilModifier; case eGpencilModifierType_Lineart: return &RNA_LineartGpencilModifier; + case eGpencilModifierType_Dash: + return &RNA_DashGpencilModifierData; /* Default */ case eGpencilModifierType_None: case NUM_GREASEPENCIL_MODIFIER_TYPES: @@ -282,19 +290,19 @@ static void rna_GpencilModifier_name_set(PointerRNA *ptr, const char *value) GpencilModifierData *gmd = ptr->data; char oldname[sizeof(gmd->name)]; - /* make a copy of the old name first */ + /* Make a copy of the old name first. */ BLI_strncpy(oldname, gmd->name, sizeof(gmd->name)); - /* copy the new name into the name slot */ + /* Copy the new name into the name slot. */ BLI_strncpy_utf8(gmd->name, value, sizeof(gmd->name)); - /* make sure the name is truly unique */ + /* Make sure the name is truly unique. */ if (ptr->owner_id) { Object *ob = (Object *)ptr->owner_id; BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, gmd); } - /* fix all the animation data which may link to this */ + /* Fix all the animation data which may link to this. */ BKE_animdata_fix_paths_rename_all(NULL, "grease_pencil_modifiers", oldname, gmd->name); } @@ -674,6 +682,59 @@ static void rna_Lineart_end_level_set(PointerRNA *ptr, int value) lmd->level_start = MIN2(value, lmd->level_start); } +static void rna_GpencilDash_segments_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + DashGpencilModifierData *dmd = (DashGpencilModifierData *)ptr->data; + rna_iterator_array_begin( + iter, dmd->segments, sizeof(DashGpencilModifierSegment), dmd->segments_len, false, NULL); +} + +static char *rna_DashGpencilModifierSegment_path(PointerRNA *ptr) +{ + DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; + + DashGpencilModifierData *dmd = (DashGpencilModifierData *)ds->dmd; + + BLI_assert(dmd != NULL); + + char name_esc[sizeof(dmd->modifier.name) * 2 + 1]; + + BLI_str_escape(name_esc, dmd->modifier.name, sizeof(name_esc)); + + return BLI_sprintfN("grease_pencil_modifiers[\"%s\"].segments[\"%s\"]", name_esc, ds->name); +} + +static bool dash_segment_name_exists_fn(void *arg, const char *name) +{ + const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg; + for (int i = 0; i < dmd->segments_len; i++) { + if (STREQ(dmd->segments[i].name, name)) { + return true; + } + } + return false; +} + +static void rna_DashGpencilModifierSegment_name_set(PointerRNA *ptr, const char *value) +{ + DashGpencilModifierSegment *ds = ptr->data; + + char oldname[sizeof(ds->name)]; + BLI_strncpy(oldname, ds->name, sizeof(ds->name)); + + BLI_strncpy_utf8(ds->name, value, sizeof(ds->name)); + + BLI_assert(ds->dmd != NULL); + BLI_uniquename_cb( + dash_segment_name_exists_fn, ds->dmd, "Segment", '.', ds->name, sizeof(ds->name)); + + char prefix[256]; + sprintf(prefix, "grease_pencil_modifiers[\"%s\"].segments", ds->dmd->modifier.name); + + /* Fix all the animation data which may link to this. */ + BKE_animdata_fix_paths_rename_all(NULL, prefix, oldname, ds->name); +} + #else static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) @@ -3287,6 +3348,136 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_define_lib_overridable(false); } +static void rna_def_modifier_gpencildash(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "DashGpencilModifierSegment", NULL); + RNA_def_struct_ui_text(srna, "Dash Modifier Segment", "Configuration for a single dash segment"); + RNA_def_struct_sdna(srna, "DashGpencilModifierSegment"); + RNA_def_struct_path_func(srna, "rna_DashGpencilModifierSegment_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the dash segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_DashGpencilModifierSegment_name_set"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "dash", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Dash", + "The number of consecutive points from the original stroke to include in this segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "gap", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 1, INT16_MAX); + RNA_def_property_ui_text(prop, "Gap", "The number of points skipped after this segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_FACTOR | PROP_UNSIGNED); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Radius", "The factor to apply to the original point's radius for the new points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_range(prop, 0, 1, 0.1, 2); + RNA_def_property_ui_text( + prop, "Opacity", "The factor to apply to the original point's opacity for the new points"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "mat_nr"); + RNA_def_property_range(prop, -1, INT16_MAX); + RNA_def_property_ui_text( + prop, + "Material Index", + "Use this index on generated segment. -1 means using the existing material"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + srna = RNA_def_struct(brna, "DashGpencilModifierData", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Dash Modifier", "Create dot-dash effect for strokes"); + RNA_def_struct_sdna(srna, "DashGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_DASH); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "DashGpencilModifierSegment"); + RNA_def_property_collection_sdna(prop, NULL, "segments", NULL); + RNA_def_property_collection_funcs(prop, + "rna_GpencilDash_segments_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "Segments", ""); + + prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Active Dash Segement Index", "Active index in the segment list"); + + prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Offset", + "Offset into each stroke before the beginning of the dashed segment generation"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Common properties. */ + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "pass_index"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + RNA_define_lib_overridable(false); +} + void RNA_def_greasepencil_modifier(BlenderRNA *brna) { StructRNA *srna; @@ -3364,6 +3555,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencilweight(brna); rna_def_modifier_gpencillineart(brna); rna_def_modifier_gpencillength(brna); + rna_def_modifier_gpencildash(brna); } #endif diff --git a/source/tools b/source/tools index 5cf2fc3e5dc..c8579c5cf43 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit 5cf2fc3e5dc28025394b57d8743401295528f310 +Subproject commit c8579c5cf43229843df505da9644b5b0b7201974 -- cgit v1.2.3 From 8cbe55c9e95ca17cc016e65406130bc0b466cb06 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 16:51:44 +1000 Subject: Cleanup: correct tracker ID in comment --- source/blender/blenkernel/intern/font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index b6e374d158f..709ae6e9494 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -721,7 +721,7 @@ typedef struct VFontToCurveIter { * so floating-point error doesn't cause unexpected wrapping, see T89241. * * \note This should only be set once, in the #VFONT_TO_CURVE_INIT pass - * otherwise iterations wont behave predictably, see T89241. + * otherwise iterations wont behave predictably, see T91401. */ bool word_wrap; int status; -- cgit v1.2.3 From 785e7ddf10540584ddc2403af40545366f32a770 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 17:04:07 +1000 Subject: Cleanup: replace defines with functions --- source/blender/blenkernel/intern/font.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 709ae6e9494..842a701f525 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -753,8 +753,15 @@ enum { * * The em_height here is relative to FT_Face->bbox. */ -#define ASCENT(vfd) ((vfd)->ascender * (vfd)->em_height) -#define DESCENT(vfd) ((vfd)->em_height - ASCENT(vfd)) + +static float vfont_ascent(const VFontData *vfd) +{ + return vfd->ascender * vfd->em_height; +} +static float vfont_descent(const VFontData *vfd) +{ + return vfd->em_height - vfont_ascent(vfd); +} static bool vfont_to_curve(Object *ob, Curve *cu, @@ -1237,17 +1244,17 @@ static bool vfont_to_curve(Object *ob, case CU_ALIGN_Y_TOP_BASELINE: break; case CU_ALIGN_Y_TOP: - yoff = textbox_y_origin - ASCENT(vfd); + yoff = textbox_y_origin - vfont_ascent(vfd); break; case CU_ALIGN_Y_CENTER: - yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - ASCENT(vfd)) - + yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - vfont_ascent(vfd)) - (tb_scale.h * 0.5f) + textbox_y_origin); break; case CU_ALIGN_Y_BOTTOM_BASELINE: yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h; break; case CU_ALIGN_Y_BOTTOM: - yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + DESCENT(vfd); + yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + vfont_descent(vfd); break; } @@ -1268,16 +1275,16 @@ static bool vfont_to_curve(Object *ob, case CU_ALIGN_Y_TOP_BASELINE: break; case CU_ALIGN_Y_TOP: - yoff = -ASCENT(vfd); + yoff = -vfont_ascent(vfd); break; case CU_ALIGN_Y_CENTER: - yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd); + yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - vfont_ascent(vfd); break; case CU_ALIGN_Y_BOTTOM_BASELINE: yoff = (lnr - 1) * linedist; break; case CU_ALIGN_Y_BOTTOM: - yoff = (lnr - 1) * linedist + DESCENT(vfd); + yoff = (lnr - 1) * linedist + vfont_descent(vfd); break; } -- cgit v1.2.3 From fb27a9bb983ce74b8d8f5f871cf0706dd1e25051 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 17:44:35 +1000 Subject: Gizmo: show groups flagged with SHOW_MODAL_ALL during interaction Follow up to fix for T73684, which allowed some modal gizmos to hide all others. Also resolve an issue from 917a972b56af103aee406dfffe1f42745b5ad360 where shear the shear gizmo would be visible during interaction. Internally there are some changes to gizmo behavior - The gizmo with modal interaction wont draw if it's poll function fails. - The WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL flag now causes these gizmo groups to draw when another group is being interacted with. --- source/blender/makesrna/intern/rna_wm_gizmo.c | 2 +- source/blender/windowmanager/gizmo/WM_gizmo_types.h | 5 ++++- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 21 +++++++-------------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index febb0e14e07..43848ce78e2 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1401,7 +1401,7 @@ static void rna_def_gizmogroup(BlenderRNA *brna) "SHOW_MODAL_ALL", 0, "Show Modal All", - "Show all while interacting"}, + "Show all while interacting, as well as this group when another is being interacted with"}, {WM_GIZMOGROUPTYPE_TOOL_INIT, "TOOL_INIT", 0, diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index eab62ffce4c..b0dd7be4572 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -115,7 +115,10 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { WM_GIZMOGROUPTYPE_SELECT = (1 << 3), /** The gizmo group is to be kept (not removed on loading a new file for eg). */ WM_GIZMOGROUPTYPE_PERSISTENT = (1 << 4), - /** Show all other gizmos when interacting. */ + /** + * Show all other gizmos when interacting. + * Also show this group when another group is being interacted with. + */ WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL = (1 << 5), /** * When used with tool, only run when activating the tool, diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 6f6a2402d89..1144cd072e0 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -381,20 +381,6 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap, wmGizmo *gz_modal = gzmap->gzmap_context.modal; - /* only active gizmo needs updating */ - if (gz_modal) { - if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) { - if ((gz_modal->parent_gzgroup->hide.any == 0) && - wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) { - if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) { - gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW; - } - } - /* don't draw any other gizmos */ - return; - } - } - /* Allow refresh functions to ask to be refreshed again, clear before the loop below. */ const bool do_refresh = gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK; gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_REFRESH_CALLBACK; @@ -406,6 +392,13 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap, continue; } + /* When modal only show other gizmo groups tagged with #WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL. */ + if (gz_modal && (gzgroup != gz_modal->parent_gzgroup)) { + if ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) { + continue; + } + } + /* Needs to be initialized on first draw. */ /* XXX weak: Gizmo-group may skip refreshing if it's invisible * (map gets untagged nevertheless). */ -- cgit v1.2.3 From e6ca0545904fe4236be9960fdb3c2760092582a5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Sep 2021 11:02:39 +0200 Subject: Geometry Nodes: multi threaded field evaluation This adds a new `ParallelMultiFunction` which wraps another multi-function and evaluates it with multiple threads. The speeds up field evaluation quite a bit (the effect is most noticeable when the number of evaluations and the field is large). There are still other single-threaded performance bottlenecks in field evaluation that will need to be solved separately. Most notably here is the process of copying the computed data into the position attribute in the Set Position node. Differential Revision: https://developer.blender.org/D12457 --- source/blender/blenlib/BLI_index_mask.hh | 3 + source/blender/blenlib/CMakeLists.txt | 1 + source/blender/blenlib/intern/index_mask.cc | 57 +++++++++++++ .../blender/blenlib/tests/BLI_index_mask_test.cc | 24 ++++++ source/blender/functions/CMakeLists.txt | 18 ++++ .../blender/functions/FN_generic_virtual_array.hh | 46 +++++++++++ .../functions/FN_multi_function_parallel.hh | 39 +++++++++ source/blender/functions/intern/field.cc | 11 ++- .../functions/intern/generic_virtual_array.cc | 43 ++++++++++ .../functions/intern/multi_function_parallel.cc | 95 ++++++++++++++++++++++ 10 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 source/blender/blenlib/intern/index_mask.cc create mode 100644 source/blender/functions/FN_multi_function_parallel.hh create mode 100644 source/blender/functions/intern/multi_function_parallel.cc diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 7a3169520ca..ad030e127fe 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -39,6 +39,7 @@ #include "BLI_index_range.hh" #include "BLI_span.hh" +#include "BLI_vector.hh" namespace blender { @@ -221,6 +222,8 @@ class IndexMask { { return indices_.is_empty(); } + + IndexMask slice_and_offset(IndexRange slice, Vector &r_new_indices) const; }; } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index fc058793d11..f607285c4d0 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -86,6 +86,7 @@ set(SRC intern/hash_md5.c intern/hash_mm2a.c intern/hash_mm3.c + intern/index_mask.cc intern/jitter_2d.c intern/kdtree_1d.c intern/kdtree_2d.c diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc new file mode 100644 index 00000000000..cba985b8a44 --- /dev/null +++ b/source/blender/blenlib/intern/index_mask.cc @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#include "BLI_index_mask.hh" + +namespace blender { + +/** + * Create a sub-mask that is also shifted to the beginning. The shifting to the beginning allows + * code to work with smaller indices, which is more memory efficient. + * + * \return New index mask with the size of #slice. It is either empty or starts with 0. It might + * reference indices that have been appended to #r_new_indices. + * + * Example: + * this: [2, 3, 5, 7, 8, 9, 10] + * slice: ^--------^ + * output: [0, 2, 4, 5] + * + * All the indices in the sub-mask are shifted by 3 towards zero, so that the first index in the + * output is zero. + */ +IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector &r_new_indices) const +{ + const int slice_size = slice.size(); + if (slice_size == 0) { + return {}; + } + IndexMask sliced_mask{indices_.slice(slice)}; + if (sliced_mask.is_range()) { + return IndexMask(slice_size); + } + const int64_t offset = sliced_mask.indices().first(); + if (offset == 0) { + return sliced_mask; + } + r_new_indices.resize(slice_size); + for (const int i : IndexRange(slice_size)) { + r_new_indices[i] = sliced_mask[i] - offset; + } + return IndexMask(r_new_indices.as_span()); +} + +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index 4d6060e51c9..0778d71df01 100644 --- a/source/blender/blenlib/tests/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -40,4 +40,28 @@ TEST(index_mask, RangeConstructor) EXPECT_EQ(indices[2], 5); } +TEST(index_mask, SliceAndOffset) +{ + Vector indices; + { + IndexMask mask{IndexRange(10)}; + IndexMask new_mask = mask.slice_and_offset(IndexRange(3, 5), indices); + EXPECT_TRUE(new_mask.is_range()); + EXPECT_EQ(new_mask.size(), 5); + EXPECT_EQ(new_mask[0], 0); + EXPECT_EQ(new_mask[1], 1); + } + { + Vector original_indices = {2, 3, 5, 7, 8, 9, 10}; + IndexMask mask{original_indices.as_span()}; + IndexMask new_mask = mask.slice_and_offset(IndexRange(1, 4), indices); + EXPECT_FALSE(new_mask.is_range()); + EXPECT_EQ(new_mask.size(), 4); + EXPECT_EQ(new_mask[0], 0); + EXPECT_EQ(new_mask[1], 2); + EXPECT_EQ(new_mask[2], 4); + EXPECT_EQ(new_mask[3], 5); + } +} + } // namespace blender::tests diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 3c27e9d5e19..856668f01d7 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -34,6 +34,7 @@ set(SRC intern/generic_virtual_vector_array.cc intern/multi_function.cc intern/multi_function_builder.cc + intern/multi_function_parallel.cc intern/multi_function_procedure.cc intern/multi_function_procedure_builder.cc intern/multi_function_procedure_executor.cc @@ -54,6 +55,7 @@ set(SRC FN_multi_function_data_type.hh FN_multi_function_param_type.hh FN_multi_function_params.hh + FN_multi_function_parallel.hh FN_multi_function_procedure.hh FN_multi_function_procedure_builder.hh FN_multi_function_procedure_executor.hh @@ -64,6 +66,22 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index f429243e2de..703118ba23e 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -911,4 +911,50 @@ template class GVMutableArray_Typed { } }; +class GVArray_For_SlicedGVArray : public GVArray { + protected: + const GVArray &varray_; + int64_t offset_; + + public: + GVArray_For_SlicedGVArray(const GVArray &varray, const IndexRange slice) + : GVArray(varray.type(), slice.size()), varray_(varray), offset_(slice.start()) + { + BLI_assert(slice.one_after_last() <= varray.size()); + } + + /* TODO: Add #materialize method. */ + void get_impl(const int64_t index, void *r_value) const override; + void get_to_uninitialized_impl(const int64_t index, void *r_value) const override; +}; + +/** + * Utility class to create the "best" sliced virtual array. + */ +class GVArray_Slice { + private: + const GVArray *varray_; + /* Of these optional virtual arrays, at most one is constructed at any time. */ + std::optional varray_span_; + std::optional varray_any_; + + public: + GVArray_Slice(const GVArray &varray, const IndexRange slice); + + const GVArray &operator*() + { + return *varray_; + } + + const GVArray *operator->() + { + return varray_; + } + + operator const GVArray &() + { + return *varray_; + } +}; + } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_parallel.hh b/source/blender/functions/FN_multi_function_parallel.hh new file mode 100644 index 00000000000..84c57efd434 --- /dev/null +++ b/source/blender/functions/FN_multi_function_parallel.hh @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#pragma once + +/** \file + * \ingroup fn + */ + +#include "FN_multi_function.hh" + +namespace blender::fn { + +class ParallelMultiFunction : public MultiFunction { + private: + const MultiFunction &fn_; + const int64_t grain_size_; + bool threading_supported_; + + public: + ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size); + + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + +} // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 43f28efd002..6a4518ad4a6 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -21,6 +21,7 @@ #include "BLI_vector_set.hh" #include "FN_field.hh" +#include "FN_multi_function_parallel.hh" namespace blender::fn { @@ -360,7 +361,13 @@ Vector evaluate_fields(ResourceScope &scope, build_multi_function_procedure_for_fields( procedure, scope, field_tree_info, varying_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; - MFParamsBuilder mf_params{procedure_executor, &mask}; + /* Add multi threading capabilities to the field evaluation. */ + const int grain_size = 10000; + fn::ParallelMultiFunction parallel_procedure_executor{procedure_executor, grain_size}; + /* Utility variable to make easy to switch the executor. */ + const MultiFunction &executor_fn = parallel_procedure_executor; + + MFParamsBuilder mf_params{executor_fn, &mask}; MFContextBuilder mf_context; /* Provide inputs to the procedure executor. */ @@ -401,7 +408,7 @@ Vector evaluate_fields(ResourceScope &scope, mf_params.add_uninitialized_single_output(span); } - procedure_executor.call(mask, mf_params, mf_context); + executor_fn.call(mask, mf_params, mf_context); } /* Evaluate constant fields if necessary. */ diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index bd033a429de..9a83d8cd497 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -387,4 +387,47 @@ void GVMutableArray_GSpan::disable_not_applied_warning() show_not_saved_warning_ = false; } +/* -------------------------------------------------------------------- + * GVArray_For_SlicedGVArray. + */ + +void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const +{ + varray_.get(index + offset_, r_value); +} + +void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, void *r_value) const +{ + varray_.get_to_uninitialized(index + offset_, r_value); +} + +/* -------------------------------------------------------------------- + * GVArray_Slice. + */ + +GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) +{ + if (varray.is_span()) { + /* Create a new virtual for the sliced span. */ + const GSpan span = varray.get_internal_span(); + const GSpan sliced_span = span.slice(slice.start(), slice.size()); + varray_span_.emplace(sliced_span); + varray_ = &*varray_span_; + } + else if (varray.is_single()) { + /* Can just use the existing virtual array, because it's the same value for the indices in the + * slice anyway. */ + varray_ = &varray; + } + else { + /* Generic version when none of the above method works. + * We don't necessarily want to materialize the input varray because there might be + * large distances between the required indices. Then we would materialize many elements that + * are not accessed later on. + */ + varray_any_.emplace(varray, slice); + varray_ = &*varray_any_; + } +} + } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_parallel.cc b/source/blender/functions/intern/multi_function_parallel.cc new file mode 100644 index 00000000000..5a8c621f0b3 --- /dev/null +++ b/source/blender/functions/intern/multi_function_parallel.cc @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#include "FN_multi_function_parallel.hh" + +#include "BLI_task.hh" + +namespace blender::fn { + +ParallelMultiFunction::ParallelMultiFunction(const MultiFunction &fn, const int64_t grain_size) + : fn_(fn), grain_size_(grain_size) +{ + this->set_signature(&fn.signature()); + + threading_supported_ = true; + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + if (param_type.data_type().category() == MFDataType::Vector) { + /* Vector parameters do not support threading yet. */ + threading_supported_ = false; + break; + } + } +} + +void ParallelMultiFunction::call(IndexMask full_mask, MFParams params, MFContext context) const +{ + if (full_mask.size() <= grain_size_ || !threading_supported_) { + fn_.call(full_mask, params, context); + return; + } + + threading::parallel_for(full_mask.index_range(), grain_size_, [&](const IndexRange mask_slice) { + Vector sub_mask_indices; + const IndexMask sub_mask = full_mask.slice_and_offset(mask_slice, sub_mask_indices); + if (sub_mask.is_empty()) { + return; + } + const int64_t input_slice_start = full_mask[mask_slice.first()]; + const int64_t input_slice_size = full_mask[mask_slice.last()] - input_slice_start + 1; + const IndexRange input_slice_range{input_slice_start, input_slice_size}; + + MFParamsBuilder sub_params{fn_, sub_mask.min_array_size()}; + ResourceScope &scope = sub_params.resource_scope(); + + /* All parameters are sliced so that the wrapped multi-function does not have to take care of + * the index offset. */ + for (const int param_index : fn_.param_indices()) { + const MFParamType param_type = fn_.param_type(param_index); + switch (param_type.category()) { + case MFParamType::SingleInput: { + const GVArray &varray = params.readonly_single_input(param_index); + const GVArray &sliced_varray = scope.construct(varray, input_slice_range); + sub_params.add_readonly_single_input(sliced_varray); + break; + } + case MFParamType::SingleMutable: { + const GMutableSpan span = params.single_mutable(param_index); + const GMutableSpan sliced_span = span.slice(input_slice_start, input_slice_size); + sub_params.add_single_mutable(sliced_span); + break; + } + case MFParamType::SingleOutput: { + const GMutableSpan span = params.uninitialized_single_output(param_index); + const GMutableSpan sliced_span = span.slice(input_slice_start, input_slice_size); + sub_params.add_uninitialized_single_output(sliced_span); + break; + } + case MFParamType::VectorInput: + case MFParamType::VectorMutable: + case MFParamType::VectorOutput: { + BLI_assert_unreachable(); + break; + } + } + } + + fn_.call(sub_mask, sub_params, context); + }); +} + +} // namespace blender::fn -- cgit v1.2.3 From 429136c89f653a0aee3253dae7ea90d524d11003 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 15 Sep 2021 11:51:52 +0200 Subject: Fix Asset Browser cannot open containing file anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In {rB9cff9f9f5df0} asset_library was renamed → asset_library_ref. Missed to update this in assets.py. Differential Revision: https://developer.blender.org/D12497 --- release/scripts/startup/bl_operators/assets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index 8c76018b7a1..b241e537c38 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -85,9 +85,9 @@ class ASSET_OT_open_containing_blend_file(Operator): @classmethod def poll(cls, context): asset_file_handle = getattr(context, 'asset_file_handle', None) - asset_library = getattr(context, 'asset_library', None) + asset_library_ref = getattr(context, 'asset_library_ref', None) - if not asset_library: + if not asset_library_ref: cls.poll_message_set("No asset library selected") return False if not asset_file_handle: @@ -100,13 +100,13 @@ class ASSET_OT_open_containing_blend_file(Operator): def execute(self, context): asset_file_handle = context.asset_file_handle - asset_library = context.asset_library + asset_library_ref = context.asset_library_ref if asset_file_handle.local_id: self.report({'WARNING'}, "This asset is stored in the current blend file") return {'CANCELLED'} - asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library) + asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) self.open_in_new_blender(asset_lib_path) wm = context.window_manager -- cgit v1.2.3 From c420399f4da8f1190dd75751845dd78e100c7e2e Mon Sep 17 00:00:00 2001 From: YimingWu Date: Wed, 15 Sep 2021 18:19:24 +0800 Subject: Fix T91421: Length modifier bake influence check. Reviewed By: Antonio Vazquez (antoniov) Differential Revision: https://developer.blender.org/D12496 --- .../gpencil_modifiers/intern/MOD_gpencillength.c | 39 ++++++++++------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 857c683d95a..908918b8591 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -108,27 +108,6 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke } } -static void bakeModifier(Main *UNUSED(bmain), - Depsgraph *UNUSED(depsgraph), - GpencilModifierData *md, - Object *ob) -{ - - bGPdata *gpd = ob->data; - - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - applyLength(lmd, gpd, gps); - } - } - } -} - -/* -------------------------------- */ - -/* Generic "generateStrokes" callback */ static void deformStroke(GpencilModifierData *md, Depsgraph *UNUSED(depsgraph), Object *ob, @@ -154,6 +133,24 @@ static void deformStroke(GpencilModifierData *md, } } +static void bakeModifier(Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) +{ + + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } +} + static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { LengthGpencilModifierData *mmd = (LengthGpencilModifierData *)md; -- cgit v1.2.3 From 1bd28a5e0ca6d16c21b6365b389c9c1830635a58 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 22:34:05 +1000 Subject: Cleanup: unused variable --- source/blender/gpencil_modifiers/intern/MOD_gpencillength.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 908918b8591..6aa0e6c152e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -143,7 +143,6 @@ static void bakeModifier(Main *UNUSED(bmain), LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { deformStroke(md, depsgraph, ob, gpl, gpf, gps); } -- cgit v1.2.3 From 5c6cc931b2203d97aa9d570ff3b353c2a3dfebed Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Sep 2021 22:34:21 +1000 Subject: Gizmo: add flag to hide the gizmo group during interaction This allows a hack to be removed that temporarily overwrote the 3D views gizmo display flag. Also reverse change from fb27a9bb983ce74b8d8f5f871cf0706dd1e25051 that runs poll on modal gizmo groups as there is some risk that the poll function unlinks the gizmo. --- source/blender/editors/mesh/editmesh_bevel.c | 10 ---------- source/blender/editors/mesh/editmesh_bisect.c | 11 ++--------- source/blender/editors/mesh/editmesh_inset.c | 10 ---------- source/blender/editors/space_image/space_image.c | 8 ++++---- source/blender/editors/transform/transform.h | 3 --- source/blender/editors/transform/transform_generics.c | 13 ------------- source/blender/editors/transform/transform_gizmo_3d.c | 12 ++++++------ source/blender/makesdna/DNA_view3d_types.h | 8 -------- source/blender/makesrna/intern/rna_wm_gizmo.c | 5 +++++ source/blender/windowmanager/gizmo/WM_gizmo_types.h | 12 ++++++++---- .../blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 17 ++++++++++++----- 11 files changed, 37 insertions(+), 72 deletions(-) diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index cdbbdd820b7..0d74187b50e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -97,7 +97,6 @@ typedef struct { int launch_event; float mcenter[2]; void *draw_handle_pixel; - short gizmo_flag; short value_mode; /* Which value does mouse movement and numeric input affect? */ float segments; /* Segments as float so smooth mouse pan works in small increments */ @@ -307,11 +306,6 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->draw_handle_pixel = ED_region_draw_cb_activate( region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - - if (v3d) { - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; - } } return true; @@ -433,15 +427,11 @@ static void edbm_bevel_exit(bContext *C, wmOperator *op) } if (opdata->is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup); } ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel); - if (v3d) { - v3d->gizmo_flag = opdata->gizmo_flag; - } G.moving = 0; } MEM_SAFE_FREE(opdata->ob_store); diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index f525f2c2e91..5cb57594ec3 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -72,7 +72,6 @@ typedef struct { bool is_dirty; } * backup; int backup_len; - short gizmo_flag; } BisectData; static void mesh_bisect_interactive_calc(bContext *C, @@ -157,8 +156,6 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & OPERATOR_RUNNING_MODAL) { - View3D *v3d = CTX_wm_view3d(C); - wmGesture *gesture = op->customdata; BisectData *opdata; @@ -181,8 +178,6 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Misc other vars. */ G.moving = G_TRANSFORM_EDIT; - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; /* Initialize modal callout. */ ED_workspace_status_text(C, TIP_("LMB: Click and drag to draw cut line")); @@ -191,10 +186,8 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event) return ret; } -static void edbm_bisect_exit(bContext *C, BisectData *opdata) +static void edbm_bisect_exit(BisectData *opdata) { - View3D *v3d = CTX_wm_view3d(C); - v3d->gizmo_flag = opdata->gizmo_flag; G.moving = 0; for (int ob_index = 0; ob_index < opdata->backup_len; ob_index++) { @@ -225,7 +218,7 @@ static int mesh_bisect_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (ret & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) { - edbm_bisect_exit(C, &opdata_back); + edbm_bisect_exit(&opdata_back); #ifdef USE_GIZMO /* Setup gizmos */ diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 159e84db4ef..7be169f70f4 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -76,7 +76,6 @@ typedef struct { int launch_event; float mcenter[2]; void *draw_handle_pixel; - short gizmo_flag; } InsetData; static void edbm_inset_update_header(wmOperator *op, bContext *C) @@ -177,7 +176,6 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->num_input.unit_type[1] = B_UNIT_LENGTH; if (is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { @@ -189,10 +187,6 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal) opdata->draw_handle_pixel = ED_region_draw_cb_activate( region->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; - if (v3d) { - opdata->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; - } } return true; @@ -206,15 +200,11 @@ static void edbm_inset_exit(bContext *C, wmOperator *op) opdata = op->customdata; if (opdata->is_modal) { - View3D *v3d = CTX_wm_view3d(C); ARegion *region = CTX_wm_region(C); for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup); } ED_region_draw_cb_exit(region->type, opdata->draw_handle_pixel); - if (v3d) { - v3d->gizmo_flag = opdata->gizmo_flag; - } G.moving = 0; } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 4107fd619aa..de8e4684d45 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -461,7 +461,7 @@ static void IMAGE_GGT_gizmo2d(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo"; gzgt->idname = "IMAGE_GGT_gizmo2d"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -475,7 +475,7 @@ static void IMAGE_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) gzgt->name = "UV Translate Gizmo"; gzgt->idname = "IMAGE_GGT_gizmo2d_translate"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -489,7 +489,7 @@ static void IMAGE_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo Resize"; gzgt->idname = "IMAGE_GGT_gizmo2d_resize"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; @@ -503,7 +503,7 @@ static void IMAGE_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) gzgt->name = "UV Transform Gizmo Resize"; gzgt->idname = "IMAGE_GGT_gizmo2d_rotate"; - gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + gzgt->flag |= (WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); gzgt->gzmap_params.spaceid = SPACE_IMAGE; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 013c5faa54a..d1a1937cef1 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -618,9 +618,6 @@ typedef struct TransInfo { O_SET, } orient_curr; - /** backup from view3d, to restore on end. */ - short gizmo_flag; - short prop_mode; /** Value taken as input, either through mouse coordinates or entered as a parameter. */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index b7f579cc12f..c493b9bd102 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -249,12 +249,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->view = v3d; t->animtimer = (animscreen) ? animscreen->animtimer : NULL; - /* turn gizmo off during transform */ - if (t->flag & T_MODAL) { - t->gizmo_flag = v3d->gizmo_flag; - v3d->gizmo_flag |= V3D_GIZMO_HIDE_DEFAULT_MODAL; - } - if (t->scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) { t->flag |= T_V3D_ALIGN; } @@ -742,13 +736,6 @@ void postTrans(bContext *C, TransInfo *t) } } } - else if (t->spacetype == SPACE_VIEW3D) { - View3D *v3d = t->area->spacedata.first; - /* restore gizmo */ - if (t->flag & T_MODAL) { - v3d->gizmo_flag = t->gizmo_flag; - } - } if (t->mouse.data) { MEM_freeN(t->mouse.data); diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 8dc4f107837..0fa179c4f74 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1974,8 +1974,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) gzgt->name = "3D View: Transform Gizmo"; gzgt->idname = "VIEW3D_GGT_xform_gizmo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2216,8 +2216,8 @@ void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt) gzgt->name = "Transform Cage"; gzgt->idname = "VIEW3D_GGT_xform_cage"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -2459,8 +2459,8 @@ void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt) gzgt->name = "Transform Shear"; gzgt->idname = "VIEW3D_GGT_xform_shear"; - gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; + gzgt->flag |= WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index fbf087ca392..4d88f6f0c15 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -610,14 +610,6 @@ enum { V3D_GIZMO_HIDE_TOOL = (1 << 3), }; -/** - * Hide these gizmos when modal operators are active, - * the intention is to hide all gizmos except for navigation since from a user-perspective - * these are closer to UI-level interface elements. Hiding them makes the UI flicker, also, - * the 3D view-axis can be useful to see during interactions. - */ -#define V3D_GIZMO_HIDE_DEFAULT_MODAL (V3D_GIZMO_HIDE_CONTEXT | V3D_GIZMO_HIDE_TOOL) - /** #View3d.gizmo_show_object */ enum { V3D_GIZMO_SHOW_OBJECT_TRANSLATE = (1 << 0), diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 43848ce78e2..6a63723d174 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -1402,6 +1402,11 @@ static void rna_def_gizmogroup(BlenderRNA *brna) 0, "Show Modal All", "Show all while interacting, as well as this group when another is being interacted with"}, + {WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE, + "EXCLUDE_MODAL", + 0, + "Exclude Modal", + "Show all except this group while interacting"}, {WM_GIZMOGROUPTYPE_TOOL_INIT, "TOOL_INIT", 0, diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index b0dd7be4572..a1edc4196dc 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -120,6 +120,10 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * Also show this group when another group is being interacted with. */ WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL = (1 << 5), + + /** Don't draw this gizmo group when it is modal. */ + WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE = (1 << 6), + /** * When used with tool, only run when activating the tool, * instead of linking the gizmo while the tool is active. @@ -130,7 +134,7 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * when a tool can activate multiple operators based on the key-map. * We could even move the options into the key-map item. * ~ campbell. */ - WM_GIZMOGROUPTYPE_TOOL_INIT = (1 << 6), + WM_GIZMOGROUPTYPE_TOOL_INIT = (1 << 7), /** * This gizmo type supports using the fallback tools keymap. @@ -138,7 +142,7 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * * Often useful in combination with #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK */ - WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 7), + WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP = (1 << 8), /** * Use this from a gizmos refresh callback so we can postpone the refresh operation @@ -149,14 +153,14 @@ typedef enum eWM_GizmoFlagGroupTypeFlag { * for selection operations. This means gizmos that use this check don't interfere * with click drag events by popping up under the cursor and catching the tweak event. */ - WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 8), + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK = (1 << 9), /** * Cause continuous redraws, i.e. set the region redraw flag on every main loop iteration. This * should really be avoided by using proper region redraw tagging, notifiers and the message-bus, * however for VR it's sometimes needed. */ - WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 9), + WM_GIZMOGROUPTYPE_VR_REDRAWS = (1 << 10), } eWM_GizmoFlagGroupTypeFlag; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 1144cd072e0..295196c701b 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -387,14 +387,21 @@ static void gizmomap_prepare_drawing(wmGizmoMap *gzmap, LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) { /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */ - if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) || - !WM_gizmo_group_type_poll(C, gzgroup->type)) { + if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep)) { continue; } - /* When modal only show other gizmo groups tagged with #WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL. */ - if (gz_modal && (gzgroup != gz_modal->parent_gzgroup)) { - if ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) { + if (gz_modal && (gzgroup == gz_modal->parent_gzgroup)) { + if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE) { + continue; + } + } + else { /* Don't poll modal gizmo since some poll functions unlink. */ + if (!WM_gizmo_group_type_poll(C, gzgroup->type)) { + continue; + } + /* When modal only show other gizmo groups tagged with #WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL. */ + if (gz_modal && ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0)) { continue; } } -- cgit v1.2.3 From 46fff97604ae167473ecd4c0cd9108dc20ba22e3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Sep 2021 16:09:00 +0200 Subject: Nodes: refactor socket declarations This commits adds a few common flags to `SocketDeclaration` so that they are available for all socket types (hide label, hide value, is multi input). This allows porting over the remaining geometry nodes to the new declaration system. Furthermore, this commit separates the concepts of the socket declaration and corresponding builders. The builders are used by nodes to declare which sockets they have (e.g. `FloatBuilder`). The ready build socket declarations can then be consumed by other systems such as the versioning code. Both use cases need different APIs and those will change for independent reasons, so it makes sense to separate the classes. --- source/blender/nodes/NOD_node_declaration.hh | 98 +++++++-- source/blender/nodes/NOD_socket_declarations.hh | 220 +++++++++++---------- .../geometry/nodes/node_geo_attribute_remove.cc | 29 +-- .../nodes/node_geo_attribute_vector_rotate.cc | 48 +++-- .../nodes/geometry/nodes/node_geo_boolean.cc | 41 ++-- .../nodes/geometry/nodes/node_geo_join_geometry.cc | 27 +-- source/blender/nodes/intern/node_declaration.cc | 29 +++ .../nodes/intern/node_socket_declarations.cc | 115 +++++------ 8 files changed, 330 insertions(+), 277 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 7ba4ac52b86..8ca2b411a38 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -27,12 +27,19 @@ namespace blender::nodes { class NodeDeclarationBuilder; +/** + * Describes a single input or output socket. This is subclassed for different socket types. + */ class SocketDeclaration { protected: std::string name_; std::string identifier_; + bool hide_label_ = false; + bool hide_value_ = false; + bool is_multi_input_ = false; friend NodeDeclarationBuilder; + template friend class SocketDeclarationBuilder; public: virtual ~SocketDeclaration() = default; @@ -43,6 +50,49 @@ class SocketDeclaration { StringRefNull name() const; StringRefNull identifier() const; + + protected: + void set_common_flags(bNodeSocket &socket) const; + bool matches_common_data(const bNodeSocket &socket) const; +}; + +class BaseSocketDeclarationBuilder { + public: + virtual ~BaseSocketDeclarationBuilder() = default; +}; + +/** + * Wraps a #SocketDeclaration and provides methods to set it up correctly. + * This is separate from #SocketDeclaration, because it allows separating the API used by nodes to + * declare themselves from how the declaration is stored internally. + */ +template +class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { + protected: + using Self = typename SocketDecl::Builder; + static_assert(std::is_base_of_v); + SocketDecl *decl_; + + friend class NodeDeclarationBuilder; + + public: + Self &hide_label(bool value) + { + decl_->hide_label_ = value; + return *(Self *)this; + } + + Self &hide_value(bool value) + { + decl_->hide_value_ = value; + return *(Self *)this; + } + + Self &multi_input(bool value) + { + decl_->is_multi_input_ = value; + return *(Self *)this; + } }; using SocketDeclarationPtr = std::unique_ptr; @@ -67,12 +117,21 @@ class NodeDeclaration { class NodeDeclarationBuilder { private: NodeDeclaration &declaration_; + Vector> builders_; public: NodeDeclarationBuilder(NodeDeclaration &declaration); - template DeclType &add_input(StringRef name, StringRef identifier = ""); - template DeclType &add_output(StringRef name, StringRef identifier = ""); + template + typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); + template + typename DeclType::Builder &add_output(StringRef name, StringRef identifier = ""); + + private: + template + typename DeclType::Builder &add_socket(StringRef name, + StringRef identifier, + Vector &r_decls); }; /* -------------------------------------------------------------------- @@ -99,27 +158,34 @@ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declarati } template -inline DeclType &NodeDeclarationBuilder::add_input(StringRef name, StringRef identifier) +inline typename DeclType::Builder &NodeDeclarationBuilder::add_input(StringRef name, + StringRef identifier) { - static_assert(std::is_base_of_v); - std::unique_ptr socket_decl = std::make_unique(); - DeclType &ref = *socket_decl; - ref.name_ = name; - ref.identifier_ = identifier.is_empty() ? name : identifier; - declaration_.inputs_.append(std::move(socket_decl)); - return ref; + return this->add_socket(name, identifier, declaration_.inputs_); +} + +template +inline typename DeclType::Builder &NodeDeclarationBuilder::add_output(StringRef name, + StringRef identifier) +{ + return this->add_socket(name, identifier, declaration_.outputs_); } template -inline DeclType &NodeDeclarationBuilder::add_output(StringRef name, StringRef identifier) +inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( + StringRef name, StringRef identifier, Vector &r_decls) { static_assert(std::is_base_of_v); + using Builder = typename DeclType::Builder; std::unique_ptr socket_decl = std::make_unique(); - DeclType &ref = *socket_decl; - ref.name_ = name; - ref.identifier_ = identifier.is_empty() ? name : identifier; - declaration_.outputs_.append(std::move(socket_decl)); - return ref; + std::unique_ptr socket_decl_builder = std::make_unique(); + socket_decl_builder->decl_ = &*socket_decl; + socket_decl->name_ = name; + socket_decl->identifier_ = identifier.is_empty() ? name : identifier; + r_decls.append(std::move(socket_decl)); + Builder &socket_decl_builder_ref = *socket_decl_builder; + builders_.append(std::move(socket_decl_builder)); + return socket_decl_builder_ref; } /* -------------------------------------------------------------------- diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 11352111356..3d0cfdb5d5d 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -25,6 +25,8 @@ namespace blender::nodes::decl { +class FloatBuilder; + class Float : public SocketDeclaration { private: float default_value_ = 0.0f; @@ -32,36 +34,45 @@ class Float : public SocketDeclaration { float soft_max_value_ = FLT_MAX; PropertySubType subtype_ = PROP_NONE; + friend FloatBuilder; + public: - Float &min(const float value) + using Builder = FloatBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class FloatBuilder : public SocketDeclarationBuilder { + public: + FloatBuilder &min(const float value) { - soft_min_value_ = value; + decl_->soft_min_value_ = value; return *this; } - Float &max(const float value) + FloatBuilder &max(const float value) { - soft_max_value_ = value; + decl_->soft_max_value_ = value; return *this; } - Float &default_value(const float value) + FloatBuilder &default_value(const float value) { - default_value_ = value; + decl_->default_value_ = value; return *this; } - Float &subtype(PropertySubType subtype) + FloatBuilder &subtype(PropertySubType subtype) { - subtype_ = subtype; + decl_->subtype_ = subtype; return *this; } - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; - bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; }; +class IntBuilder; + class Int : public SocketDeclaration { private: int default_value_ = 0; @@ -69,36 +80,45 @@ class Int : public SocketDeclaration { int soft_max_value_ = INT32_MAX; PropertySubType subtype_ = PROP_NONE; + friend IntBuilder; + public: - Int &min(const int value) + using Builder = IntBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class IntBuilder : public SocketDeclarationBuilder { + public: + IntBuilder &min(const int value) { - soft_min_value_ = value; + decl_->soft_min_value_ = value; return *this; } - Int &max(const int value) + IntBuilder &max(const int value) { - soft_max_value_ = value; + decl_->soft_max_value_ = value; return *this; } - Int &default_value(const int value) + IntBuilder &default_value(const int value) { - default_value_ = value; + decl_->default_value_ = value; return *this; } - Int &subtype(PropertySubType subtype) + IntBuilder &subtype(PropertySubType subtype) { - subtype_ = subtype; + decl_->subtype_ = subtype; return *this; } - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; - bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; }; +class VectorBuilder; + class Vector : public SocketDeclaration { private: float3 default_value_ = {0, 0, 0}; @@ -106,160 +126,152 @@ class Vector : public SocketDeclaration { float soft_max_value_ = FLT_MAX; PropertySubType subtype_ = PROP_NONE; + friend VectorBuilder; + + public: + using Builder = VectorBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; +}; + +class VectorBuilder : public SocketDeclarationBuilder { public: - Vector &default_value(const float3 value) + VectorBuilder &default_value(const float3 value) { - default_value_ = value; + decl_->default_value_ = value; return *this; } - Vector &subtype(PropertySubType subtype) + VectorBuilder &subtype(PropertySubType subtype) { - subtype_ = subtype; + decl_->subtype_ = subtype; return *this; } - Vector &min(const float min) + VectorBuilder &min(const float min) { - soft_min_value_ = min; + decl_->soft_min_value_ = min; return *this; } - Vector &max(const float max) + VectorBuilder &max(const float max) { - soft_max_value_ = max; + decl_->soft_max_value_ = max; return *this; } - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; - bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; }; +class BoolBuilder; + class Bool : public SocketDeclaration { private: bool default_value_ = false; + friend BoolBuilder; public: - Bool &default_value(const bool value) - { - default_value_ = value; - return *this; - } + using Builder = BoolBuilder; bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; +class BoolBuilder : public SocketDeclarationBuilder { + public: + BoolBuilder &default_value(const bool value) + { + decl_->default_value_ = value; + return *this; + } +}; + +class ColorBuilder; + class Color : public SocketDeclaration { private: ColorGeometry4f default_value_; + friend ColorBuilder; + public: - Color &default_value(const ColorGeometry4f value) - { - default_value_ = value; - return *this; - } + using Builder = ColorBuilder; bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; +class ColorBuilder : public SocketDeclarationBuilder { + public: + ColorBuilder &default_value(const ColorGeometry4f value) + { + decl_->default_value_ = value; + return *this; + } +}; + class String : public SocketDeclaration { public: + using Builder = SocketDeclarationBuilder; + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; -namespace detail { -struct CommonIDSocketData { - const char *idname; - bool hide_label = false; -}; - -bNodeSocket &build_id_socket(bNodeTree &ntree, - bNode &node, - eNodeSocketInOut in_out, - const CommonIDSocketData &data, - StringRefNull name, - StringRefNull identifier); -bool matches_id_socket(const bNodeSocket &socket, - const CommonIDSocketData &data, - StringRefNull name, - StringRefNull identifier); - -template class IDSocketDeclaration : public SocketDeclaration { +class IDSocketDeclaration : public SocketDeclaration { private: - CommonIDSocketData data_; + const char *idname_; public: - IDSocketDeclaration(const char *idname) : data_({idname}) - { - } - - Subtype &hide_label(bool value) - { - data_.hide_label = value; - return *(Subtype *)this; - } - - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override - { - return build_id_socket(ntree, node, in_out, data_, name_, identifier_); - } - - bool matches(const bNodeSocket &socket) const override + IDSocketDeclaration(const char *idname) : idname_(idname) { - return matches_id_socket(socket, data_, name_, identifier_); } - bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override - { - if (StringRef(socket.idname) != data_.idname) { - return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); - } - if (data_.hide_label) { - socket.flag |= SOCK_HIDE_LABEL; - } - else { - socket.flag &= ~SOCK_HIDE_LABEL; - } - return socket; - } + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const override; }; -} // namespace detail -class Object : public detail::IDSocketDeclaration { +class Object : public IDSocketDeclaration { public: - Object() : detail::IDSocketDeclaration("NodeSocketObject") + using Builder = SocketDeclarationBuilder; + + Object() : IDSocketDeclaration("NodeSocketObject") { } }; -class Material : public detail::IDSocketDeclaration { +class Material : public IDSocketDeclaration { public: - Material() : detail::IDSocketDeclaration("NodeSocketMaterial") + using Builder = SocketDeclarationBuilder; + + Material() : IDSocketDeclaration("NodeSocketMaterial") { } }; -class Collection : public detail::IDSocketDeclaration { +class Collection : public IDSocketDeclaration { public: - Collection() : detail::IDSocketDeclaration("NodeSocketCollection") + using Builder = SocketDeclarationBuilder; + + Collection() : IDSocketDeclaration("NodeSocketCollection") { } }; -class Texture : public detail::IDSocketDeclaration { +class Texture : public IDSocketDeclaration { public: - Texture() : detail::IDSocketDeclaration("NodeSocketTexture") + using Builder = SocketDeclarationBuilder; + + Texture() : IDSocketDeclaration("NodeSocketTexture") { } }; class Geometry : public SocketDeclaration { public: + using Builder = SocketDeclarationBuilder; + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index e4f3230ebb9..ca205362a61 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -16,28 +16,15 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_attribute_remove_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, - N_("Attribute"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_remove_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Attribute").multi_input(true); + b.add_output("Geometry"); +} + static void remove_attribute(GeometryComponent &component, GeoNodeExecParams ¶ms, Span attribute_names) @@ -85,7 +72,7 @@ void register_node_type_geo_attribute_remove() geo_node_type_base( &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; + ntype.declare = blender::nodes::geo_node_attribute_remove_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc index da753dfc11b..ddeef267240 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -21,27 +21,26 @@ #include "UI_interface.h" #include "UI_resources.h" -static bNodeSocketTemplate geo_node_attribute_vector_rotate_in[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("Vector")}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_STRING, N_("Center")}, - {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_XYZ}, - {SOCK_STRING, N_("Axis")}, - {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_XYZ, PROP_NONE}, - {SOCK_STRING, N_("Angle")}, - {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE}, - {SOCK_STRING, N_("Rotation")}, - {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, - {SOCK_BOOLEAN, N_("Invert")}, - {SOCK_STRING, N_("Result")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_attribute_vector_rotate_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Vector"); + b.add_input("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(true); + b.add_input("Center"); + b.add_input("Center", "Center_001").subtype(PROP_XYZ); + b.add_input("Axis"); + b.add_input("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input("Angle"); + b.add_input("Angle", "Angle_001").subtype(PROP_ANGLE); + b.add_input("Rotation"); + b.add_input("Rotation", "Rotation_001").subtype(PROP_EULER); + b.add_input("Invert"); + b.add_input("Result"); + + b.add_output("Geometry"); +} static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, bContext *UNUSED(C), @@ -71,8 +70,6 @@ static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, } } -namespace blender::nodes { - static void geo_node_attribute_vector_rotate_update(bNodeTree *UNUSED(ntree), bNode *node) { const NodeAttributeVectorRotate *node_storage = (NodeAttributeVectorRotate *)node->storage; @@ -339,14 +336,13 @@ void register_node_type_geo_attribute_vector_rotate() "Attribute Vector Rotate", NODE_CLASS_ATTRIBUTE, 0); - node_type_socket_templates( - &ntype, geo_node_attribute_vector_rotate_in, geo_node_attribute_vector_rotate_out); node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_rotate_update); node_type_init(&ntype, blender::nodes::geo_node_attribute_vector_rotate_init); node_type_size(&ntype, 165, 100, 600); node_type_storage( &ntype, "NodeAttributeVectorRotate", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_rotate_exec; - ntype.draw_buttons = geo_node_attribute_vector_rotate_layout; + ntype.draw_buttons = blender::nodes::geo_node_attribute_vector_rotate_layout; + ntype.declare = blender::nodes::geo_node_attribute_vector_rotate_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index d8029ea1eeb..a990f1daac8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -23,27 +23,16 @@ #include "node_geometry_util.hh" -static bNodeSocketTemplate geo_node_boolean_in[] = { - {SOCK_GEOMETRY, N_("Geometry 1")}, - {SOCK_GEOMETRY, - N_("Geometry 2"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {SOCK_BOOLEAN, N_("Self Intersection")}, - {SOCK_BOOLEAN, N_("Hole Tolerant")}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_boolean_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void geo_node_boolean_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry 1"); + b.add_input("Geometry 2").multi_input(true); + b.add_input("Self Intersection"); + b.add_input("Hole Tolerant"); + b.add_output("Geometry"); +} static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { @@ -77,8 +66,6 @@ static void geo_node_boolean_init(bNodeTree *UNUSED(tree), bNode *node) node->custom1 = GEO_NODE_BOOLEAN_DIFFERENCE; } -namespace blender::nodes { - static void geo_node_boolean_exec(GeoNodeExecParams params) { GeometryNodeBooleanOperation operation = (GeometryNodeBooleanOperation)params.node().custom1; @@ -138,10 +125,10 @@ void register_node_type_geo_boolean() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_boolean_in, geo_node_boolean_out); - ntype.draw_buttons = geo_node_boolean_layout; - ntype.updatefunc = geo_node_boolean_update; - node_type_init(&ntype, geo_node_boolean_init); + ntype.declare = blender::nodes::geo_node_boolean_declare; + ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; + ntype.updatefunc = blender::nodes::geo_node_boolean_update; + node_type_init(&ntype, blender::nodes::geo_node_boolean_init); ntype.geometry_node_execute = blender::nodes::geo_node_boolean_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 5792ee1485a..3de5c89b77b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -29,27 +29,14 @@ using blender::fn::GVArray_For_GSpan; -static bNodeSocketTemplate geo_node_join_geometry_in[] = { - {SOCK_GEOMETRY, - N_("Geometry"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_MULTI_INPUT}, - {-1, ""}, -}; - -static bNodeSocketTemplate geo_node_join_geometry_out[] = { - {SOCK_GEOMETRY, N_("Geometry")}, - {-1, ""}, -}; - namespace blender::nodes { +static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry").multi_input(true); + b.add_output("Geometry"); +} + static Mesh *join_mesh_topology_and_builtin_attributes(Span src_components) { int totverts = 0; @@ -508,7 +495,7 @@ void register_node_type_geo_join_geometry() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_JOIN_GEOMETRY, "Join Geometry", NODE_CLASS_GEOMETRY, 0); - node_type_socket_templates(&ntype, geo_node_join_geometry_in, geo_node_join_geometry_out); ntype.geometry_node_execute = blender::nodes::geo_node_join_geometry_exec; + ntype.declare = blender::nodes::geo_node_join_geometry_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index dff92d5884f..f6b6cc49b2e 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -16,6 +16,8 @@ #include "NOD_node_declaration.hh" +#include "BKE_node.h" + namespace blender::nodes { void NodeDeclaration::build(bNodeTree &ntree, bNode &node) const @@ -62,4 +64,31 @@ bNodeSocket &SocketDeclaration::update_or_build(bNodeTree &ntree, return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); } +void SocketDeclaration::set_common_flags(bNodeSocket &socket) const +{ + SET_FLAG_FROM_TEST(socket.flag, hide_value_, SOCK_HIDE_VALUE); + SET_FLAG_FROM_TEST(socket.flag, hide_label_, SOCK_HIDE_LABEL); + SET_FLAG_FROM_TEST(socket.flag, is_multi_input_, SOCK_MULTI_INPUT); +} + +bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const +{ + if (socket.name != name_) { + return false; + } + if (socket.identifier != identifier_) { + return false; + } + if (((socket.flag & SOCK_HIDE_VALUE) != 0) != hide_value_) { + return false; + } + if (((socket.flag & SOCK_HIDE_LABEL) != 0) != hide_label_) { + return false; + } + if (((socket.flag & SOCK_MULTI_INPUT) != 0) != is_multi_input_) { + return false; + } + return true; +} + } // namespace blender::nodes diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index e17b0fd5579..4b0dbad3cff 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -38,6 +38,7 @@ bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_FLOAT, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; value.min = soft_min_value_; value.max = soft_max_value_; @@ -47,16 +48,13 @@ bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out bool Float::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_FLOAT) { - return false; - } - if (socket.typeinfo->subtype != subtype_) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.name != name_) { + if (socket.type != SOCK_FLOAT) { return false; } - if (socket.identifier != identifier_) { + if (socket.typeinfo->subtype != subtype_) { return false; } bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; @@ -77,6 +75,7 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket & if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); } + this->set_common_flags(socket); bNodeSocketValueFloat &value = *(bNodeSocketValueFloat *)socket.default_value; value.min = soft_min_value_; value.max = soft_max_value_; @@ -92,6 +91,7 @@ bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_INT, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; value.min = soft_min_value_; value.max = soft_max_value_; @@ -101,16 +101,13 @@ bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) bool Int::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_INT) { - return false; - } - if (socket.typeinfo->subtype != subtype_) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.name != name_) { + if (socket.type != SOCK_INT) { return false; } - if (socket.identifier != identifier_) { + if (socket.typeinfo->subtype != subtype_) { return false; } bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; @@ -131,6 +128,7 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); } + this->set_common_flags(socket); bNodeSocketValueInt &value = *(bNodeSocketValueInt *)socket.default_value; value.min = soft_min_value_; value.max = soft_max_value_; @@ -146,6 +144,7 @@ bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ou { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_VECTOR, subtype_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; copy_v3_v3(value.value, default_value_); value.min = soft_min_value_; @@ -155,16 +154,13 @@ bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ou bool Vector::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_VECTOR) { - return false; - } - if (socket.typeinfo->subtype != subtype_) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.name != name_) { + if (socket.type != SOCK_VECTOR) { return false; } - if (socket.identifier != identifier_) { + if (socket.typeinfo->subtype != subtype_) { return false; } return true; @@ -178,6 +174,7 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket if (socket.typeinfo->subtype != subtype_) { modify_subtype_except_for_storage(socket, subtype_); } + this->set_common_flags(socket); bNodeSocketValueVector &value = *(bNodeSocketValueVector *)socket.default_value; value.subtype = subtype_; STRNCPY(socket.name, name_.c_str()); @@ -192,6 +189,7 @@ bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_BOOLEAN, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); bNodeSocketValueBoolean &value = *(bNodeSocketValueBoolean *)socket.default_value; value.value = default_value_; return socket; @@ -199,13 +197,10 @@ bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) bool Bool::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_BOOLEAN) { - return false; - } - if (socket.name != name_) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.identifier != identifier_) { + if (socket.type != SOCK_BOOLEAN) { return false; } return true; @@ -219,6 +214,7 @@ bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_RGBA, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); bNodeSocketValueRGBA &value = *(bNodeSocketValueRGBA *)socket.default_value; copy_v4_v4(value.value, default_value_); return socket; @@ -226,13 +222,15 @@ bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out bool Color::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_RGBA) { - return false; - } - if (socket.name != name_) { - return false; + if (!this->matches_common_data(socket)) { + if (socket.name != name_) { + return false; + } + if (socket.identifier != identifier_) { + return false; + } } - if (socket.identifier != identifier_) { + if (socket.type != SOCK_RGBA) { return false; } return true; @@ -246,18 +244,16 @@ bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ou { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); return socket; } bool String::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_STRING) { - return false; - } - if (socket.name != name_) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.identifier != identifier_) { + if (socket.type != SOCK_STRING) { return false; } return true; @@ -267,42 +263,37 @@ bool String::matches(const bNodeSocket &socket) const * IDSocketDeclaration. */ -namespace detail { -bNodeSocket &build_id_socket(bNodeTree &ntree, - bNode &node, - eNodeSocketInOut in_out, - const CommonIDSocketData &data, - StringRefNull name, - StringRefNull identifier) +bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, + bNode &node, + eNodeSocketInOut in_out) const { bNodeSocket &socket = *nodeAddSocket( - &ntree, &node, in_out, data.idname, identifier.c_str(), name.c_str()); - if (data.hide_label) { - socket.flag |= SOCK_HIDE_LABEL; - } + &ntree, &node, in_out, idname_, identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); return socket; } -bool matches_id_socket(const bNodeSocket &socket, - const CommonIDSocketData &data, - StringRefNull name, - StringRefNull identifier) +bool IDSocketDeclaration::matches(const bNodeSocket &socket) const { - if (!STREQ(socket.idname, data.idname)) { + if (!this->matches_common_data(socket)) { return false; } - if (data.hide_label != ((socket.flag & SOCK_HIDE_LABEL) != 0)) { - return false; - } - if (socket.name != name) { - return false; - } - if (socket.identifier != identifier) { + if (!STREQ(socket.idname, idname_)) { return false; } return true; } -} // namespace detail + +bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, + bNode &node, + bNodeSocket &socket) const +{ + if (StringRef(socket.idname) != idname_) { + return this->build(ntree, node, (eNodeSocketInOut)socket.in_out); + } + this->set_common_flags(socket); + return socket; +} /* -------------------------------------------------------------------- * Geometry. @@ -312,18 +303,16 @@ bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ { bNodeSocket &socket = *nodeAddSocket( &ntree, &node, in_out, "NodeSocketGeometry", identifier_.c_str(), name_.c_str()); + this->set_common_flags(socket); return socket; } bool Geometry::matches(const bNodeSocket &socket) const { - if (socket.type != SOCK_GEOMETRY) { + if (!this->matches_common_data(socket)) { return false; } - if (socket.name != name_) { - return false; - } - if (socket.identifier != identifier_) { + if (socket.type != SOCK_GEOMETRY) { return false; } return true; -- cgit v1.2.3 From e456a9de57f0ade56a2b38f3278492b7ac692dce Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 15 Sep 2021 16:35:33 +0200 Subject: Cleanup: avoid passing redundant parameter --- source/blender/nodes/NOD_node_declaration.hh | 6 +++--- source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc | 2 +- .../nodes/geometry/nodes/node_geo_attribute_sample_texture.cc | 2 +- .../nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_boolean.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_collection_info.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_material_assign.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_object_info.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_point_instance.cc | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 8ca2b411a38..d64b76ccbb9 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -76,19 +76,19 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { friend class NodeDeclarationBuilder; public: - Self &hide_label(bool value) + Self &hide_label(bool value = true) { decl_->hide_label_ = value; return *(Self *)this; } - Self &hide_value(bool value) + Self &hide_value(bool value = true) { decl_->hide_value_ = value; return *(Self *)this; } - Self &multi_input(bool value) + Self &multi_input(bool value = true) { decl_->is_multi_input_ = value; return *(Self *)this; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index ca205362a61..21a9a338857 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -21,7 +21,7 @@ namespace blender::nodes { static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Attribute").multi_input(true); + b.add_input("Attribute").multi_input(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index c4fd0ca4008..52f97475941 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -33,7 +33,7 @@ namespace blender::nodes { static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Texture").hide_label(true); + b.add_input("Texture").hide_label(); b.add_input("Mapping"); b.add_input("Result"); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc index ddeef267240..adaa4de3029 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -27,7 +27,7 @@ static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Vector"); - b.add_input("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(true); + b.add_input("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); b.add_input("Center"); b.add_input("Center", "Center_001").subtype(PROP_XYZ); b.add_input("Axis"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index a990f1daac8..2a1c43a89fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -28,7 +28,7 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry 1"); - b.add_input("Geometry 2").multi_input(true); + b.add_input("Geometry 2").multi_input(); b.add_input("Self Intersection"); b.add_input("Hole Tolerant"); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index e731b4c0cdc..f4c295b06fb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -27,7 +27,7 @@ namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Collection").hide_label(true); + b.add_input("Collection").hide_label(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 3de5c89b77b..93643298f92 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -33,7 +33,7 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").multi_input(true); + b.add_input("Geometry").multi_input(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index d7d3d0eded8..9e99ae29b00 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -29,7 +29,7 @@ namespace blender::nodes { static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Material").hide_label(true); + b.add_input("Material").hide_label(); b.add_input("Selection"); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index ab99c9bb3f8..389acc40f0f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -25,7 +25,7 @@ namespace blender::nodes { static void geo_node_object_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Object").hide_label(true); + b.add_input("Object").hide_label(); b.add_output("Location"); b.add_output("Rotation"); b.add_output("Scale"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc index 902ccfff179..fb45c22ced4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc @@ -29,8 +29,8 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Object").hide_label(true); - b.add_input("Collection").hide_label(true); + b.add_input("Object").hide_label(); + b.add_input("Collection").hide_label(); b.add_input("Instance Geometry"); b.add_input("Seed").min(-10000).max(10000); b.add_output("Geometry"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc index 40c990346e5..1d1b9712fe0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc @@ -31,7 +31,7 @@ namespace blender::nodes { static void geo_node_select_by_material_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); - b.add_input("Material").hide_label(true); + b.add_input("Material").hide_label(); b.add_input("Selection"); b.add_output("Geometry"); } -- cgit v1.2.3 From 5841f8656d9580d7b967d477b142f19364ec39b5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 10:28:00 -0500 Subject: Geometry Nodes: Add special domain interpolation for selections The generic domain interpolation algorithms didn't quite work for selections. The interpolation would do unexpected things that were different than the results in edit mode. The new behavior is supposed to be the same as edit mode, although we also have to handle face corner selections here. Currently the code assumes that all boolean attributes should be handled that way. I'm not sure of why that wouldn't be the case, but if we ever need non-selection boolean attributes, that could be supported too. Differential Revision: https://developer.blender.org/D12488 --- .../blenkernel/intern/geometry_component_curve.cc | 31 +++ .../blenkernel/intern/geometry_component_mesh.cc | 236 ++++++++++++++++++++- 2 files changed, 263 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index afafd766760..7d0537178ef 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -222,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, mixer.finalize(); } +/** + * A spline is selected if all of its control points were selected. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<> +void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, + const VArray &old_values, + MutableSpan r_values) +{ + const int splines_len = curve.splines().size(); + Array offsets = curve.control_point_offsets(); + BLI_assert(r_values.size() == splines_len); + + r_values.fill(true); + + for (const int i_spline : IndexRange(splines_len)) { + const int spline_offset = offsets[i_spline]; + const int spline_point_len = offsets[i_spline + 1] - spline_offset; + + for (const int i_point : IndexRange(spline_point_len)) { + if (!old_values[spline_offset + i_point]) { + r_values[i_spline] = false; + break; + } + } + } +} + static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) { GVArrayPtr new_varray; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9a4b8f4eb92..25ef4b6000f 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if all connected face corners were selected and it is not loose. */ +template<> +void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + Array loose_verts(mesh.totvert, true); + + r_values.fill(true); + for (const int loop_index : IndexRange(mesh.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int point_index = loop.v; + + loose_verts[point_index] = false; + if (!old_values[loop_index]) { + r_values[point_index] = false; + } + } + + /* Deselect loose vertices without corners that are still selected from the 'true' default. */ + for (const int vert_index : IndexRange(mesh.totvert)) { + if (loose_verts[vert_index]) { + r_values[vert_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr return new_varray; } +/** + * Each corner's value is simply a copy of the value at its vertex. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ template static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, const VArray &old_values, @@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr GVArrayPtr new_varray; attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); - /* It is not strictly necessary to compute the value for all corners here. Instead one could - * lazily lookup the mesh topology when a specific index accessed. This can be more efficient - * when an algorithm only accesses very few of the corner values. However, for the algorithms - * we currently have, precomputing the array is fine. Also, it is easier to implement. */ Array values(mesh.totloop); adapt_mesh_domain_point_to_corner_impl(mesh, varray->typed(), values); new_varray = std::make_unique>>(std::move(values)); @@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its corners were selected. */ +template<> +void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + if (!old_values[loop_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if all corners on adjacent faces were selected. */ +template<> +void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */ + Array loose_edges(mesh.totedge, true); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + loose_edges[edge_index] = false; + + if (!old_values[loop_index] || !old_values[loop_index_next]) { + r_values[edge_index] = false; + } + } + } + + /* Deselect loose edges without corners that are still selected from the 'true' default. */ + for (const int edge_index : IndexRange(mesh.totedge)) { + if (loose_edges[edge_index]) { + r_values[edge_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any of the connected faces were selected. */ +template<> +void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + r_values[vert_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v return new_varray; } +/* Each corner's value is simply a copy of the value at its face. */ template void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, const VArray &old_values, @@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if any connected face was selected. */ +template<> +void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + r_values[edge_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its vertices were selected too. */ +template<> +void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + if (!old_values[vert_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -452,6 +603,20 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if both of its vertices were selected. */ +template<> +void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2]; + } +} + static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -490,6 +655,29 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } +/* A corner is selected if its two adjacent edges were selected. */ +template<> +void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totloop); + + r_values.fill(false); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop; + const MLoop &loop = mesh.mloop[loop_index]; + const MLoop &loop_prev = mesh.mloop[loop_index_prev]; + if (old_values[loop.e] && old_values[loop_prev.e]) { + r_values[loop_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -522,6 +710,24 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any connected edge was selected. */ +template<> +void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + if (old_values[edge_index]) { + r_values[edge.v1] = true; + r_values[edge.v2] = true; + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -560,6 +766,28 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its edges are selected. */ +template<> +void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, + const VArray &old_values, + MutableSpan r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + if (!old_values[edge_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; -- cgit v1.2.3 From 09f14b38f2d0c22b396dcd6c15bf959631ed1bfd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 10:51:52 -0500 Subject: Geometry Nodes: Add fields version of material nodes This commit moves the old material nodes to a "legacy" folder and adds versions of the nodes that work with fields. The "Select by Material" node is a field node now, so it doesn't have a geometry output. This is an improvement because there are fewer links to connect, and it's more easily usable in different situations. It's also called "Material Selection", since it's more of an input than an action now. It's sometimes necessary to use the attribute capture node to get a more predictable interpolation to mesh faces. This is because the selection field input is always evaluated on the face domain, so attribute inputs are interpolated before they are booleans, so they cannot use the new interpolations from rB5841f8656d9580d7b9. Differential Revision: https://developer.blender.org/D12456 --- release/scripts/startup/nodeitems_builtins.py | 2 + source/blender/blenkernel/BKE_node.h | 2 + source/blender/blenkernel/intern/node.cc | 5 +- source/blender/nodes/CMakeLists.txt | 5 +- source/blender/nodes/NOD_geometry.h | 5 +- source/blender/nodes/NOD_static_types.h | 2 + .../nodes/legacy/node_geo_material_assign.cc | 95 +++++++++++++++ .../nodes/legacy/node_geo_select_by_material.cc | 92 +++++++++++++++ .../geometry/nodes/node_geo_material_assign.cc | 29 +++-- .../geometry/nodes/node_geo_material_selection.cc | 131 +++++++++++++++++++++ .../geometry/nodes/node_geo_select_by_material.cc | 92 --------------- 11 files changed, 352 insertions(+), 108 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc create mode 100644 source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc create mode 100644 source/blender/nodes/geometry/nodes/node_geo_material_selection.cc delete mode 100644 source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c0af91b7ad6..569c5291576 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -564,6 +564,8 @@ geometry_node_categories = [ NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll), NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll), NodeItem("GeometryNodeMaterialReplace"), ]), GeometryNodeCategory("GEO_MESH", "Mesh", items=[ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d4bc0245a61..a57281e4478 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1491,6 +1491,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_INPUT_INDEX 1078 #define GEO_NODE_INPUT_NORMAL 1079 #define GEO_NODE_ATTRIBUTE_CAPTURE 1080 +#define GEO_NODE_MATERIAL_SELECTION 1081 +#define GEO_NODE_MATERIAL_ASSIGN 1082 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 61b90b44b03..0d691854c1d 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5153,6 +5153,9 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_material_assign(); + register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_attribute_clamp(); register_node_type_geo_attribute_color_ramp(); @@ -5225,7 +5228,7 @@ static void registerGeometryNodes() register_node_type_geo_raycast(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); - register_node_type_geo_select_by_material(); + register_node_type_geo_material_selection(); register_node_type_geo_separate_components(); register_node_type_geo_set_position(); register_node_type_geo_subdivision_surface(); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 614be1d7229..b741461f820 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -141,6 +141,9 @@ set(SRC function/nodes/node_fn_random_float.cc function/node_function_util.cc + geometry/nodes/legacy/node_geo_material_assign.cc + geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/node_geo_align_rotation_to_vector.cc geometry/nodes/node_geo_attribute_capture.cc geometry/nodes/node_geo_attribute_clamp.cc @@ -195,6 +198,7 @@ set(SRC geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_assign.cc geometry/nodes/node_geo_material_replace.cc + geometry/nodes/node_geo_material_selection.cc geometry/nodes/node_geo_mesh_primitive_circle.cc geometry/nodes/node_geo_mesh_primitive_cone.cc geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -214,7 +218,6 @@ set(SRC geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc geometry/nodes/node_geo_raycast.cc - geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc geometry/nodes/node_geo_set_position.cc geometry/nodes/node_geo_subdivision_surface.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index e9fb4ad123c..a713da45f0b 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,6 +29,9 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_select_by_material(void); + void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_clamp(void); void register_node_type_geo_attribute_color_ramp(void); @@ -80,6 +83,7 @@ void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); +void register_node_type_geo_material_selection(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); @@ -101,7 +105,6 @@ void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); -void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); void register_node_type_geo_set_position(void); void register_node_type_geo_subdivision_surface(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 215dca63fa3..51d59821d3c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -332,7 +332,9 @@ DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "No DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") +DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") +DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc new file mode 100644 index 00000000000..7d3481c1067 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_material.h" + +namespace blender::nodes { + +static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Material").hide_label(true); + b.add_input("Selection"); + b.add_output("Geometry"); +} + +static void assign_material_to_faces(Mesh &mesh, const VArray &face_mask, Material *material) +{ + int new_material_index = -1; + for (const int i : IndexRange(mesh.totcol)) { + Material *other_material = mesh.mat[i]; + if (other_material == material) { + new_material_index = i; + break; + } + } + if (new_material_index == -1) { + /* Append a new material index. */ + new_material_index = mesh.totcol; + BKE_id_material_eval_assign(&mesh.id, new_material_index + 1, material); + } + + mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); + for (const int i : IndexRange(mesh.totpoly)) { + if (face_mask[i]) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; + } + } +} + +static void geo_node_legacy_material_assign_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input("Material"); + const std::string mask_name = params.extract_input("Selection"); + + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + Mesh *mesh = mesh_component.get_for_write(); + if (mesh != nullptr) { + GVArray_Typed face_mask = mesh_component.attribute_get_for_read( + mask_name, ATTR_DOMAIN_FACE, true); + assign_material_to_faces(*mesh, face_mask, material); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_material_assign() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_material_assign_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_material_assign_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc new file mode 100644 index 00000000000..eabdd2bcd5a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_task.hh" + +#include "BKE_material.h" + +namespace blender::nodes { + +static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Geometry"); + b.add_input("Material").hide_label(); + b.add_input("Selection"); + b.add_output("Geometry"); +} + +static void select_mesh_by_material(const Mesh &mesh, + const Material *material, + const MutableSpan r_selection) +{ + BLI_assert(mesh.totpoly == r_selection.size()); + Vector material_indices; + for (const int i : IndexRange(mesh.totcol)) { + if (mesh.mat[i] == material) { + material_indices.append(i); + } + } + threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr); + } + }); +} + +static void geo_node_legacy_select_by_material_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input("Material"); + const std::string selection_name = params.extract_input("Selection"); + + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh != nullptr) { + OutputAttribute_Typed selection = + mesh_component.attribute_try_get_for_output_only(selection_name, ATTR_DOMAIN_FACE); + if (selection) { + select_mesh_by_material(*mesh, material, selection.as_span()); + selection.save(); + } + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_select_by_material() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_select_by_material_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_select_by_material_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index 9e99ae29b00..bdcf06fe416 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -30,11 +30,11 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Material").hide_label(); - b.add_input("Selection"); + b.add_input("Selection").default_value(true); b.add_output("Geometry"); } -static void assign_material_to_faces(Mesh &mesh, const VArray &face_mask, Material *material) +static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) { int new_material_index = -1; for (const int i : IndexRange(mesh.totcol)) { @@ -51,18 +51,16 @@ static void assign_material_to_faces(Mesh &mesh, const VArray &face_mask, } mesh.mpoly = (MPoly *)CustomData_duplicate_referenced_layer(&mesh.pdata, CD_MPOLY, mesh.totpoly); - for (const int i : IndexRange(mesh.totpoly)) { - if (face_mask[i]) { - MPoly &poly = mesh.mpoly[i]; - poly.mat_nr = new_material_index; - } + for (const int i : selection) { + MPoly &poly = mesh.mpoly[i]; + poly.mat_nr = new_material_index; } } static void geo_node_material_assign_exec(GeoNodeExecParams params) { Material *material = params.extract_input("Material"); - const std::string mask_name = params.extract_input("Selection"); + const Field selection_field = params.extract_input>("Selection"); GeometrySet geometry_set = params.extract_input("Geometry"); @@ -72,9 +70,15 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params) MeshComponent &mesh_component = geometry_set.get_component_for_write(); Mesh *mesh = mesh_component.get_for_write(); if (mesh != nullptr) { - GVArray_Typed face_mask = mesh_component.attribute_get_for_read( - mask_name, ATTR_DOMAIN_FACE, true); - assign_material_to_faces(*mesh, face_mask, material); + + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; + + fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + assign_material_to_faces(*mesh, selection, material); } } @@ -87,8 +91,7 @@ void register_node_type_geo_material_assign() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_assign_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc new file mode 100644 index 00000000000..22c24e34314 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_task.hh" + +#include "BKE_material.h" + +namespace blender::nodes { + +static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_input("Material").hide_label(true); + b.add_output("Selection"); +} + +static void select_mesh_by_material(const Mesh &mesh, + const Material *material, + const IndexMask mask, + const MutableSpan r_selection) +{ + BLI_assert(mesh.totpoly >= r_selection.size()); + Vector material_indices; + for (const int i : IndexRange(mesh.totcol)) { + if (mesh.mat[i] == material) { + material_indices.append(i); + } + } + threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) { + for (const int i : range) { + const int face_index = mask[i]; + r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr); + } + }); +} + +class MaterialSelectionFieldInput final : public fn::FieldInput { + Material *material_; + + public: + MaterialSelectionFieldInput(Material *material) + : fn::FieldInput(CPPType::get(), "Material Selection"), material_(material) + { + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return nullptr; + } + const MeshComponent &mesh_component = static_cast(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_FACE) { + Array selection(mask.min_array_size()); + select_mesh_by_material(*mesh, material_, mask, selection); + return &scope.construct>>(std::move(selection)); + } + + Array selection(mesh->totpoly); + select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection); + GVArrayPtr face_selection = std::make_unique>>( + std::move(selection)); + GVArrayPtr final_selection = mesh_component.attribute_try_adapt_domain( + std::move(face_selection), ATTR_DOMAIN_FACE, domain); + return scope.add_value(std::move(final_selection)).get(); + } + + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 91619626; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_material_selection_exec(GeoNodeExecParams params) +{ + Material *material = params.extract_input("Material"); + Field material_field{std::make_shared(material)}; + params.set_output("Selection", std::move(material_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_material_selection() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_MATERIAL_SELECTION, "Material Selection", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_material_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_material_selection_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc deleted file mode 100644 index 1d1b9712fe0..00000000000 --- a/source/blender/nodes/geometry/nodes/node_geo_select_by_material.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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. - */ - -#include "node_geometry_util.hh" - -#include "UI_interface.h" -#include "UI_resources.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_task.hh" - -#include "BKE_material.h" - -namespace blender::nodes { - -static void geo_node_select_by_material_declare(NodeDeclarationBuilder &b) -{ - b.add_input("Geometry"); - b.add_input("Material").hide_label(); - b.add_input("Selection"); - b.add_output("Geometry"); -} - -static void select_mesh_by_material(const Mesh &mesh, - const Material *material, - const MutableSpan r_selection) -{ - BLI_assert(mesh.totpoly == r_selection.size()); - Vector material_indices; - for (const int i : IndexRange(mesh.totcol)) { - if (mesh.mat[i] == material) { - material_indices.append(i); - } - } - threading::parallel_for(r_selection.index_range(), 1024, [&](IndexRange range) { - for (const int i : range) { - r_selection[i] = material_indices.contains(mesh.mpoly[i].mat_nr); - } - }); -} - -static void geo_node_select_by_material_exec(GeoNodeExecParams params) -{ - Material *material = params.extract_input("Material"); - const std::string selection_name = params.extract_input("Selection"); - - GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); - - if (geometry_set.has()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - const Mesh *mesh = mesh_component.get_for_read(); - if (mesh != nullptr) { - OutputAttribute_Typed selection = - mesh_component.attribute_try_get_for_output_only(selection_name, ATTR_DOMAIN_FACE); - if (selection) { - select_mesh_by_material(*mesh, material, selection.as_span()); - selection.save(); - } - } - } - - params.set_output("Geometry", std::move(geometry_set)); -} - -} // namespace blender::nodes - -void register_node_type_geo_select_by_material() -{ - static bNodeType ntype; - - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, "Select by Material", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_select_by_material_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_select_by_material_exec; - nodeRegisterType(&ntype); -} -- cgit v1.2.3 From a6adb7ecaef38e419d9268074193942669be6e7f Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 15 Sep 2021 11:13:10 -0500 Subject: BLI: Add a reverse method to MutableSpan Add a method that allows a MutableSpan to reverse itself. This reverses the data in the original span object. This is a first step in extracting some functionality from nodes and making it more general. Differential Revision: https://developer.blender.org/D12485 --- source/blender/blenlib/BLI_span.hh | 10 +++++ source/blender/blenlib/tests/BLI_span_test.cc | 23 +++++++++++ .../nodes/geometry/nodes/node_geo_curve_reverse.cc | 46 ++++++---------------- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index e04295b0e51..5adb47ba0b0 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -643,6 +643,16 @@ template class MutableSpan { return MutableSpan(data_ + size_ - new_size, new_size); } + /** + * Reverse the data in the MutableSpan. + */ + constexpr void reverse() + { + for (const int i : IndexRange(size_ / 2)) { + std::swap(data_[size_ - 1 - i], data_[i]); + } + } + /** * Returns an (immutable) Span that references the same array. This is usually not needed, * due to implicit conversions. However, sometimes automatic type deduction needs some help. diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 4d23a53c08a..fb88fb63e53 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -362,6 +362,29 @@ TEST(span, ReverseIterator) EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); } +TEST(span, ReverseMutableSpan) +{ + std::array src0 = {}; + MutableSpan span0 = src0; + span0.reverse(); + EXPECT_EQ_ARRAY(span0.data(), Span({}).data(), 0); + + std::array src1 = {4}; + MutableSpan span1 = src1; + span1.reverse(); + EXPECT_EQ_ARRAY(span1.data(), Span({4}).data(), 1); + + std::array src2 = {4, 5}; + MutableSpan span2 = src2; + span2.reverse(); + EXPECT_EQ_ARRAY(span2.data(), Span({5, 4}).data(), 2); + + std::array src5 = {4, 5, 6, 7, 8}; + MutableSpan span5 = src5; + span5.reverse(); + EXPECT_EQ_ARRAY(span5.data(), Span({8, 7, 6, 5, 4}).data(), 5); +} + TEST(span, MutableReverseIterator) { std::array src = {4, 5, 6, 7}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 2cb75eda202..70bb9bd28f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -29,31 +29,6 @@ static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) b.add_output("Curve"); } -/** - * Reverse the data in a MutableSpan object. - */ -template static void reverse_data(MutableSpan r_data) -{ - const int size = r_data.size(); - for (const int i : IndexRange(size / 2)) { - std::swap(r_data[size - 1 - i], r_data[i]); - } -} - -/** - * Reverse and Swap the data between 2 MutableSpans. - */ -template static void reverse_data(MutableSpan left, MutableSpan right) -{ - BLI_assert(left.size() == right.size()); - const int size = left.size(); - - for (const int i : IndexRange(size / 2 + size % 2)) { - std::swap(left[i], right[size - 1 - i]); - std::swap(right[i], left[size - 1 - i]); - } -} - static void geo_node_curve_reverse_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Curve"); @@ -78,9 +53,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) continue; } - reverse_data(splines[i]->positions()); - reverse_data(splines[i]->radii()); - reverse_data(splines[i]->tilts()); + splines[i]->positions().reverse(); + splines[i]->radii().reverse(); + splines[i]->tilts().reverse(); splines[i]->attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { @@ -92,7 +67,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) } attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { using T = decltype(dummy); - reverse_data(output_attribute->typed()); + output_attribute->typed().reverse(); }); return true; }, @@ -100,12 +75,17 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) /* Deal with extra info on derived types. */ if (BezierSpline *spline = dynamic_cast(splines[i].get())) { - reverse_data(spline->handle_types_left()); - reverse_data(spline->handle_types_right()); - reverse_data(spline->handle_positions_left(), spline->handle_positions_right()); + spline->handle_types_left().reverse(); + spline->handle_types_right().reverse(); + + spline->handle_positions_left().reverse(); + spline->handle_positions_right().reverse(); + for (int i : spline->handle_positions_left().index_range()) { + std::swap(spline->handle_positions_left()[i], spline->handle_positions_right()[i]); + } } else if (NURBSpline *spline = dynamic_cast(splines[i].get())) { - reverse_data(spline->weights()); + spline->weights().reverse(); } /* Nothing to do for poly splines. */ -- cgit v1.2.3 From b6d890672c5fcc7a970d0475a9bdb0d44950ad50 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 15 Sep 2021 18:45:11 +0200 Subject: Fix crash in 'drag asset' case in new append code from yesterday. Scene and related pointer parameter can be NULL in link/append code, in which case there is no instantiation of new objects/collections/obdata. Link code in blendloader was already checking that, new instantiation code in WM area from yesterday did not. Issue introduced by rB3be5ce4aad5e. --- source/blender/windowmanager/intern/wm_files_link.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 2416f5b50b3..7568c9989a8 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -383,6 +383,12 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, ViewLayer *view_layer, const View3D *v3d) { + if (scene == NULL) { + /* In some cases, like the asset drag&drop e.g., the caller code manages instantiation itself. + */ + return; + } + LinkNode *itemlink; Collection *active_collection = NULL; const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0; @@ -1281,6 +1287,10 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, return id; } +/* + * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no + * instantiation of linked objects, collections etc. will be performed. + */ ID *WM_file_link_datablock(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -1293,6 +1303,10 @@ ID *WM_file_link_datablock(Main *bmain, bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); } +/* + * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no + * instantiation of appended objects, collections etc. will be performed. + */ ID *WM_file_append_datablock(Main *bmain, Scene *scene, ViewLayer *view_layer, -- cgit v1.2.3 From 51f7d24a4ecb89059751b2432408680bc5275902 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 15 Sep 2021 17:42:17 +0200 Subject: Fix missing passes result when rendering multiple views Caused by the lazily pass pixels allocation which didn't reset allocation state of the render result. Demo file: XXX --- source/blender/render/intern/render_result.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index c29ab342ed7..6cb6aabe885 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -250,6 +250,9 @@ RenderPass *render_layer_add_pass(RenderResult *rr, BLI_addtail(&rl->passes, rpass); + /* The result contains non-allocated pass now, so tag it as such. */ + rr->passes_allocated = false; + return rpass; } -- cgit v1.2.3 From 4a009b54bc8ecac548c52cda590c2500787ebf2d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 15 Sep 2021 19:02:05 +0200 Subject: Fix broken WorkSpace use after recent refactor of append code. Essentially, Workspace (and Screen) types were defined as not localizable. In previous code it happended to work by mere chance (code path taken in `BKE_library_make_local` was conviniently skipping the call to `BKE_lib_id_make_local` in that case, hence not checking for `IDTYPE_FLAGS_NO_MAKELOCAL` flag of the localized ID type). This is a total abuse of this IDType flag, for now removing it. That specific case (IDtype appendable but nor linkable) requires its own proper flag, this will be tackled in a later commit. Issue introduced in rB3be5ce4aad5e. --- source/blender/blenkernel/intern/screen.c | 2 +- source/blender/blenkernel/intern/workspace.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 065240bddbc..60da008a282 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -312,7 +312,7 @@ IDTypeInfo IDType_ID_SCR = { .name = "Screen", .name_plural = "screens", .translation_context = BLT_I18NCONTEXT_ID_SCREEN, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 329633c6759..d7368e62437 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_WS = { .name = "WorkSpace", .name_plural = "workspaces", .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = workspace_init_data, .copy_data = NULL, -- cgit v1.2.3 From a866a32ff257fd9ff4d543adbddad283a189bbc6 Mon Sep 17 00:00:00 2001 From: Himanshi Kalra Date: Wed, 15 Sep 2021 23:04:47 +0530 Subject: Tests: Updated test message for Directory Not Found --- tests/python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 79632e49c1f..a1b94abc317 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -766,7 +766,7 @@ foreach(geo_node_test ${geo_node_tests}) ) endforeach() else() - MESSAGE(STATUS "No directory named ${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/ found, disabling test.") + MESSAGE(STATUS "Directory named ${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/ Not Found, disabling test.") endif() endforeach() -- cgit v1.2.3 From 231948f33f8f3e79f7ae38ab9b3db3e597f13b4a Mon Sep 17 00:00:00 2001 From: Johnny Matthews Date: Wed, 15 Sep 2021 13:57:01 -0500 Subject: Splines: Add a method for reversing a Spline This moved the spline reversing logic out of the Curve Reverse geometry node and into the spline class. This allows a spline to reverse itself with a call to `my_spline.reverse()` The base class will reverse position, radii & tilt, while specialized versions are created for Bezier and Nurbs splines to reverse the additional data that these classes encapsulate. Differential Revision: https://developer.blender.org/D12501 --- source/blender/blenkernel/BKE_spline.hh | 11 ++++++ source/blender/blenkernel/intern/spline_base.cc | 29 +++++++++++++++ source/blender/blenkernel/intern/spline_bezier.cc | 11 ++++++ source/blender/blenkernel/intern/spline_nurbs.cc | 5 +++ source/blender/blenkernel/intern/spline_poly.cc | 4 +++ .../nodes/geometry/nodes/node_geo_curve_reverse.cc | 42 ++-------------------- 6 files changed, 62 insertions(+), 40 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index fc145f1ddf1..0fbf39a52fa 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -130,6 +130,11 @@ class Spline { virtual void translate(const blender::float3 &translation); virtual void transform(const blender::float4x4 &matrix); + /** + * Change the direction of the spline (switch the start and end) without changing its shape. + */ + void reverse(); + /** * Mark all caches for re-computation. This must be called after any operation that would * change the generated positions, tangents, normals, mapping, etc. of the evaluated points. @@ -210,6 +215,7 @@ class Spline { virtual void correct_end_tangents() const = 0; virtual void copy_settings(Spline &dst) const = 0; virtual void copy_data(Spline &dst) const = 0; + virtual void reverse_impl() = 0; }; /** @@ -353,6 +359,9 @@ class BezierSpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + + protected: + void reverse_impl() override; }; /** @@ -469,6 +478,7 @@ class NURBSpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + void reverse_impl() override; void calculate_knots() const; blender::Span calculate_basis_cache() const; @@ -519,6 +529,7 @@ class PolySpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; + void reverse_impl() override; }; /** diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 732fabc6582..a8871777420 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -19,6 +19,8 @@ #include "BLI_task.hh" #include "BLI_timeit.hh" +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" #include "BKE_spline.hh" #include "FN_generic_virtual_array.hh" @@ -28,6 +30,8 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::attribute_math::convert_to_static_type; +using blender::bke::AttributeIDRef; using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray; @@ -110,6 +114,31 @@ void Spline::transform(const blender::float4x4 &matrix) this->mark_cache_invalid(); } +void Spline::reverse() +{ + this->positions().reverse(); + this->radii().reverse(); + this->tilts().reverse(); + + this->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + std::optional attribute = this->attributes.get_for_write(id); + if (!attribute) { + BLI_assert_unreachable(); + return false; + } + convert_to_static_type(meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + attribute->typed().reverse(); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + this->reverse_impl(); + this->mark_cache_invalid(); +} + int Spline::evaluated_edges_size() const { const int eval_size = this->evaluated_points_size(); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index b6764f65631..79d2137ee84 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -166,6 +166,17 @@ MutableSpan BezierSpline::handle_positions_right() return handle_positions_right_; } +void BezierSpline::reverse_impl() +{ + this->handle_positions_left().reverse(); + this->handle_positions_right().reverse(); + std::swap(this->handle_positions_left_, this->handle_positions_right_); + + this->handle_types_left().reverse(); + this->handle_types_right().reverse(); + std::swap(this->handle_types_left_, this->handle_types_right_); +} + static float3 previous_position(Span positions, const bool cyclic, const int i) { if (i == 0) { diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index ac6f1bd082c..6d30d8ba916 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -142,6 +142,11 @@ Span NURBSpline::weights() const return weights_; } +void NURBSpline::reverse_impl() +{ + this->weights().reverse(); +} + void NURBSpline::mark_cache_invalid() { basis_cache_dirty_ = true; diff --git a/source/blender/blenkernel/intern/spline_poly.cc b/source/blender/blenkernel/intern/spline_poly.cc index dfd24b2566e..338b5d0ac9e 100644 --- a/source/blender/blenkernel/intern/spline_poly.cc +++ b/source/blender/blenkernel/intern/spline_poly.cc @@ -91,6 +91,10 @@ Span PolySpline::tilts() const return tilts_; } +void PolySpline::reverse_impl() +{ +} + void PolySpline::mark_cache_invalid() { tangent_cache_dirty_ = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 70bb9bd28f4..32bcbe2c608 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -49,47 +49,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - if (!selection[i]) { - continue; + if (selection[i]) { + splines[i]->reverse(); } - - splines[i]->positions().reverse(); - splines[i]->radii().reverse(); - splines[i]->tilts().reverse(); - - splines[i]->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - std::optional output_attribute = - splines[i]->attributes.get_for_write(attribute_id); - if (!output_attribute) { - BLI_assert_unreachable(); - return false; - } - attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) { - using T = decltype(dummy); - output_attribute->typed().reverse(); - }); - return true; - }, - ATTR_DOMAIN_POINT); - - /* Deal with extra info on derived types. */ - if (BezierSpline *spline = dynamic_cast(splines[i].get())) { - spline->handle_types_left().reverse(); - spline->handle_types_right().reverse(); - - spline->handle_positions_left().reverse(); - spline->handle_positions_right().reverse(); - for (int i : spline->handle_positions_left().index_range()) { - std::swap(spline->handle_positions_left()[i], spline->handle_positions_right()[i]); - } - } - else if (NURBSpline *spline = dynamic_cast(splines[i].get())) { - spline->weights().reverse(); - } - /* Nothing to do for poly splines. */ - - splines[i]->mark_cache_invalid(); } }); -- cgit v1.2.3 From acb8909021b188658ba92031e9544f91ff872f90 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 14:12:48 -0500 Subject: Fix: Division by zero in curve spiral primitive node --- source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 169a169bb1c..0803d43e5c3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -41,7 +41,7 @@ static std::unique_ptr create_spiral_curve(const float rotations, std::unique_ptr curve = std::make_unique(); std::unique_ptr spline = std::make_unique(); - const int totalpoints = resolution * rotations; + const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; float radius = start_radius; const float delta_height = height / (float)totalpoints; -- cgit v1.2.3 From 525def99bc907d4768b00632a3e9f8e1eb47669c Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 14:16:16 -0500 Subject: Geometry Nodes: Hide values for selection inputs Toggling the selection off in the node is the same as muting it, so exposing it there doesn't help, and makes it less clear that it's meant to be used as a field. --- source/blender/nodes/geometry/nodes/node_geo_material_assign.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_set_position.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc index bdcf06fe416..43818947272 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc @@ -30,7 +30,7 @@ static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Material").hide_label(); - b.add_input("Selection").default_value(true); + b.add_input("Selection").default_value(true).hide_value(); b.add_output("Geometry"); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index e8591616f55..4c754ddb643 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -24,7 +24,7 @@ static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { b.add_input("Geometry"); b.add_input("Position"); - b.add_input("Selection").default_value(true); + b.add_input("Selection").default_value(true).hide_value(); b.add_output("Geometry"); } -- cgit v1.2.3 From 8c5d9fa92919020293f2639caa77ec2f9e65ae0f Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 14:19:08 -0500 Subject: Cleanup: Remove duplicate warning from subdivision surface node --- .../blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index d127f7dc0ba..4541bf3569f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -37,14 +37,13 @@ static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { -#ifndef WITH_OPENSUBDIV - UNUSED_VARS(ptr); - uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR); -#else +#ifdef WITH_OPENSUBDIV uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#else + UNUSED_VARS(layout, ptr); #endif } -- cgit v1.2.3 From 28bd74c18650a8362bc791df602097967ff5efdf Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 15 Sep 2021 14:44:56 -0500 Subject: Cleanup: Use function to mark mesh normals dirty --- source/blender/blenkernel/intern/dynamicpaint.c | 2 +- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/blenkernel/intern/geometry_component_mesh.cc | 2 +- source/blender/blenkernel/intern/mesh.c | 4 ++-- source/blender/blenkernel/intern/mesh_convert.c | 2 +- source/blender/blenkernel/intern/subdiv_mesh.c | 2 +- source/blender/editors/sculpt_paint/paint_mask.c | 2 +- source/blender/modifiers/intern/MOD_array.c | 2 +- source/blender/modifiers/intern/MOD_bevel.c | 2 +- source/blender/modifiers/intern/MOD_boolean.cc | 6 +++--- source/blender/modifiers/intern/MOD_build.c | 2 +- source/blender/modifiers/intern/MOD_decimate.c | 2 +- source/blender/modifiers/intern/MOD_edgesplit.c | 2 +- source/blender/modifiers/intern/MOD_mask.cc | 3 +-- source/blender/modifiers/intern/MOD_mirror.c | 2 +- source/blender/modifiers/intern/MOD_normal_edit.c | 2 +- source/blender/modifiers/intern/MOD_ocean.c | 4 ++-- source/blender/modifiers/intern/MOD_particleinstance.c | 2 +- source/blender/modifiers/intern/MOD_remesh.c | 2 +- source/blender/modifiers/intern/MOD_screw.c | 4 ++-- source/blender/modifiers/intern/MOD_skin.c | 2 +- source/blender/modifiers/intern/MOD_solidify_extrude.c | 2 +- source/blender/modifiers/intern/MOD_solidify_nonmanifold.c | 2 +- source/blender/modifiers/intern/MOD_triangulate.c | 2 +- source/blender/modifiers/intern/MOD_util.c | 1 - source/blender/modifiers/intern/MOD_weld.c | 3 +-- source/blender/modifiers/intern/MOD_wireframe.c | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 3 +-- source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_transform.cc | 3 +-- 30 files changed, 34 insertions(+), 39 deletions(-) diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 0dc4f64cec1..d75b3259148 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2064,7 +2064,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object * } if (update_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } /* make a copy of mesh to use as brush data */ diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index f13ed1f2bf7..1324b37f39c 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3573,7 +3573,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje } BKE_mesh_calc_edges(result, false, false); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 25ef4b6000f..0c98aa5551b 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -926,7 +926,7 @@ static void tag_normals_dirty_when_writing_position(GeometryComponent &component { Mesh *mesh = get_mesh_from_component_for_write(component); if (mesh != nullptr) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 2efe0d77d87..b21f8a71174 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1859,7 +1859,7 @@ void BKE_mesh_vert_coords_apply(Mesh *mesh, const float (*vert_coords)[3]) for (int i = 0; i < mesh->totvert; i++, mv++) { copy_v3_v3(mv->co, vert_coords[i]); } - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, @@ -1872,7 +1872,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh, for (int i = 0; i < mesh->totvert; i++, mv++) { mul_v3_m4v3(mv->co, mat, vert_coords[i]); } - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } void BKE_mesh_vert_normals_apply(Mesh *mesh, const short (*vert_normals)[3]) diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index 9fd75be0d35..50c80f8d0a4 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -525,7 +525,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * } mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); if (totvert != 0) { memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index da6ee8d8779..e9cd0b70019 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -1232,7 +1232,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, // BKE_mesh_validate(result, true, true); BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH); if (!subdiv_context.can_evaluate_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } /* Free used memory. */ subdiv_mesh_context_free(&subdiv_context); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index d968b6cc319..4a8dcd7a934 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1300,7 +1300,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) }), sculpt_mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); BKE_mesh_nomain_to_mesh( result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true); } diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 6a9c9715994..2f0f11ab56d 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -786,7 +786,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, * TODO: we may need to set other dirty flags as well? */ if (use_recalc_normals) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } if (vgroup_start_cap_remap) { diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index 8fdd222402e..add95a0d248 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -243,7 +243,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index bdb791dc8e7..c5d6902e1bc 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -161,7 +161,7 @@ static Mesh *get_quick_mesh( mul_m4_v3(omat, mv->co); } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } break; @@ -506,7 +506,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } if (result == nullptr) { @@ -541,7 +541,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } } diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index a344a15b0c1..6cd8d70383d 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -281,7 +281,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct MEM_freeN(faceMap); if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } /* TODO(sybren): also copy flags & tags? */ diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index faad1175f3a..56fcbbd8b7c 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -222,7 +222,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * TIMEIT_END(decim); #endif - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 82a6e169a7a..b21a536ad8a 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -115,7 +115,7 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 306e79aa647..9a8af35109a 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -814,8 +814,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) } BKE_mesh_calc_edges_loose(result); - /* Tag to recalculate normals later. */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 6116cf8146a..7fd90c71c9f 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -117,7 +117,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = mirrorModifier__doMirror(mmd, ctx->object, mesh); if (result != mesh) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; } diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 1dbdcf87d63..db2eedf9c02 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -450,7 +450,7 @@ static void normalEditModifier_do_directional(NormalEditModifierData *enmd, if (do_polynors_fix && polygons_check_flip(mloop, nos, &mesh->ldata, mpoly, polynors, num_polys)) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } BKE_mesh_normals_loop_custom_set(mvert, diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index 1c502b94bdb..ff1055eff3b 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -317,7 +317,7 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co } } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } @@ -510,7 +510,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * result = doOcean(md, ctx, mesh); if (result != mesh) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 49b5dabe72d..4fffa7c93f3 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -545,7 +545,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * MEM_SAFE_FREE(vert_part_index); MEM_SAFE_FREE(vert_part_value); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index df3db894f4e..fef1f76c051 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -220,7 +220,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 0819b314e32..f24f6951690 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1135,12 +1135,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * ob_axis != NULL ? mtx_tx[3] : NULL, ltmd->merge_dist); if (result != result_prev) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } } if ((ltmd->flag & MOD_SCREW_NORMAL_CALC) == 0) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } return result; diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 543cee18868..7d90935f678 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1960,7 +1960,7 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, origmesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); skin_set_orig_indices(result); diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 00fa6e24a64..8f9aa86e561 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -988,7 +988,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex /* must recalculate normals with vgroups since they can displace unevenly T26888. */ if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || do_rim || dvert) { - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); } else if (do_shell) { uint i; diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 5b4716a1a43..f654b69841e 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1955,7 +1955,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); /* Make edges. */ { diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index ef633494c7b..52d5f3e97ef 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.c +++ b/source/blender/modifiers/intern/MOD_triangulate.c @@ -107,7 +107,7 @@ Mesh *triangulate_mesh(Mesh *mesh, me->flag |= ME_EDGEDRAW | ME_EDGERENDER; } - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 5b97d0eb259..d57e92b4b35 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -216,7 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, * we really need vertexCos here. */ else if (vertexCos) { BKE_mesh_vert_coords_apply(mesh, vertexCos); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; } if (use_orco) { diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index b1fa2a7d912..503297d5985 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -1979,8 +1979,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, BLI_assert(loop_cur == result_nloops); /* is this needed? */ - /* recalculate normals */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); weld_mesh_context_free(&weld_mesh); } diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index e188a61e975..706960182cf 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -109,7 +109,7 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index b0c763c7d06..2eae11d1705 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -294,8 +294,7 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr BKE_id_material_eval_ensure_default_slot(&mesh->id); mesh->flag |= ME_AUTOSMOOTH; mesh->smoothresh = DEG2RADF(180.0f); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { for (const int i_spline : curves_range) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 5845eb357e0..1e2f652cd78 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -559,7 +559,7 @@ static Mesh *delete_mesh_selection(const Mesh &mesh_in, mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); BKE_mesh_calc_edges_loose(result); /* Tag to recalculate normals later. */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(result); return result; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d7423aa6d32..d5eb067cad0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -69,8 +69,7 @@ void transform_mesh(Mesh *mesh, else { const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); BKE_mesh_transform(mesh, matrix.values, false); - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + BKE_mesh_normals_tag_dirty(mesh); } } -- cgit v1.2.3 From 289f68588a7c016f28bca9f719f96c4478d96d0a Mon Sep 17 00:00:00 2001 From: YimingWu Date: Thu, 16 Sep 2021 08:40:37 +0800 Subject: GPencil: Fix dash modifier reading error. The reference for parent modifier in segment data is not assigned. Now fixed. --- source/blender/blenkernel/intern/gpencil_modifier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index b120c901499..6be03bffb3c 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -1025,6 +1025,9 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb) else if (md->type == eGpencilModifierType_Dash) { DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md; BLO_read_data_address(reader, &gpmd->segments); + for (int i = 0; i < gpmd->segments_len; i++) { + gpmd->segments[i].dmd = gpmd; + } } } } -- cgit v1.2.3 From 0530b679092e44bda2bf67b5096573718d108364 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Sep 2021 16:36:53 +1000 Subject: Fix bisect gizmo offset while dragging --- source/blender/editors/mesh/editmesh_bisect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 5cb57594ec3..27a1bf9658f 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -777,7 +777,7 @@ static void MESH_GGT_bisect(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Bisect"; gzgt->idname = "MESH_GGT_bisect"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; -- cgit v1.2.3 From 0e329a967e8defef7dbaf417f4187e2281123a43 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Sep 2021 16:38:02 +1000 Subject: Fix memory leak if an error occurred assigning id-property sequence --- source/blender/python/generic/idprop_py_api.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 4296474c011..8dc0d2fb857 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -533,6 +533,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) { + IDP_FreeProperty(prop); return NULL; } } @@ -545,6 +546,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (((prop_data[i] = PyC_Long_AsI32(item)) == -1) && PyErr_Occurred()) { + IDP_FreeProperty(prop); return NULL; } } @@ -555,6 +557,7 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) for (i = 0; i < val.array.len; i++) { item = ob_seq_fast_items[i]; if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) { + IDP_FreeProperty(prop); return NULL; } } -- cgit v1.2.3 From 860a55d8fccfb73e747e1dc302015e8347c2f0f0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Sep 2021 16:52:28 +1000 Subject: Fix security popup re-displaying after undo Don't reset these flags when loading a file (or undoing) rely on BPY_python_reset to reset the flags. --- source/blender/blenkernel/BKE_global.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 89713e9ad0a..7696b5c0189 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -124,7 +124,10 @@ enum { /** Don't overwrite these flags when reading a file. */ #define G_FLAG_ALL_RUNTIME \ (G_FLAG_SCRIPT_AUTOEXEC | G_FLAG_SCRIPT_OVERRIDE_PREF | G_FLAG_EVENT_SIMULATE | \ - G_FLAG_USERPREF_NO_SAVE_ON_EXIT) + G_FLAG_USERPREF_NO_SAVE_ON_EXIT | \ +\ + /* #BPY_python_reset is responsible for resetting these flags on file load. */ \ + G_FLAG_SCRIPT_AUTOEXEC_FAIL | G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET) /** Flags to read from blend file. */ #define G_FLAG_ALL_READFILE 0 -- cgit v1.2.3 From bfe8f29bafa7f214a35196ba1f095dac026254b2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 16 Sep 2021 21:45:52 +1000 Subject: Fix ID-property UI versioning skipping nested meta-strips --- source/blender/blenloader/intern/versioning_300.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 4eba9f5f42e..55aed4ddc2b 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -239,6 +239,16 @@ static void do_versions_idproperty_bones_recursive(Bone *bone) } } +static void do_versions_idproperty_seq_recursive(ListBase *seqbase) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + version_idproperty_ui_data(seq->prop); + if (seq->type == SEQ_TYPE_META) { + do_versions_idproperty_seq_recursive(&seq->seqbase); + } + } +} + /** * For every data block that supports them, initialize the new IDProperty UI data struct based on * the old more complicated storage. Assumes only the top level of IDProperties below the parent @@ -299,9 +309,7 @@ static void do_versions_idproperty_ui_data(Main *bmain) /* Sequences. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { - LISTBASE_FOREACH (Sequence *, seq, &scene->ed->seqbase) { - version_idproperty_ui_data(seq->prop); - } + do_versions_idproperty_seq_recursive(&scene->ed->seqbase); } } } -- cgit v1.2.3 From 95c2f0bea8b4991acb97ac69c1b516a7c4fd14d7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 10:50:27 +0200 Subject: IDType: Cleanup: Remove useless `IDTYPE_FLAGS_NO_MAKELOCAL`. This flag became a full duplicate of `IDTYPE_FLAGS_NO_LIBLINKING`, which is a good thing (don't think we ever want to be able to link some data, without being able to make it local...). So we can now remove it and use `IDTYPE_FLAGS_NO_LIBLINKING` instead. --- source/blender/blenkernel/BKE_idtype.h | 2 -- source/blender/blenkernel/intern/ipo.c | 3 +-- source/blender/blenkernel/intern/key.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 4 ++-- source/blender/blenkernel/intern/library.c | 3 +-- source/blender/windowmanager/intern/wm.c | 3 +-- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index b0939ec884d..66355ac82ac 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -45,8 +45,6 @@ enum { IDTYPE_FLAGS_NO_COPY = 1 << 0, /** Indicates that the given IDType does not support linking/appending from a library file. */ IDTYPE_FLAGS_NO_LIBLINKING = 1 << 1, - /** Indicates that the given IDType does not support making a library-linked ID local. */ - IDTYPE_FLAGS_NO_MAKELOCAL = 1 << 2, /** Indicates that the given IDType does not have animation data. */ IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3, }; diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index aac081991e3..9b72a2d1a72 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -184,8 +184,7 @@ IDTypeInfo IDType_ID_IP = { .name = "Ipo", .name_plural = "ipos", .translation_context = "", - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index f79058dcf21..44fc86877a7 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -212,7 +212,7 @@ IDTypeInfo IDType_ID_KE = { .name = "Key", .name_plural = "shape_keys", .translation_context = BLT_I18NCONTEXT_ID_SHAPEKEY, - .flags = IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL, + .flags = IDTYPE_FLAGS_NO_LIBLINKING, .init_data = NULL, .copy_data = shapekey_copy_data, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 4d7d675e364..6cd42b17e11 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { .name = "LinkPlaceholder", .name_plural = "link_placeholders", .translation_context = BLT_I18NCONTEXT_ID_ID, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING, .init_data = NULL, .copy_data = NULL, @@ -496,7 +496,7 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id); if (idtype_info != NULL) { - if ((idtype_info->flags & IDTYPE_FLAGS_NO_MAKELOCAL) == 0) { + if ((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) { if (!test) { if (idtype_info->make_local != NULL) { idtype_info->make_local(bmain, id, flags); diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 07a3396ad5f..36958e36004 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -68,8 +68,7 @@ IDTypeInfo IDType_ID_LI = { .name = "Library", .name_plural = "libraries", .translation_context = BLT_I18NCONTEXT_ID_LIBRARY, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index e11ef52eb84..0b7d5e5f1f4 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -266,8 +266,7 @@ IDTypeInfo IDType_ID_WM = { .name = "WindowManager", .name_plural = "window_managers", .translation_context = BLT_I18NCONTEXT_ID_WINDOWMANAGER, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL | - IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, -- cgit v1.2.3 From 4f38624bf5df66ed1cf03a7167c9f959bab21ef9 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 11:11:45 +0200 Subject: IDType: Add `BKE_idtype_idcode_is_localizable`. This is the same as `BKE_idtype_idcode_is_linkable` currently, used only in one place in UI code of IDtemplate. --- source/blender/blenkernel/BKE_idtype.h | 6 +++++- source/blender/editors/interface/interface_templates.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 66355ac82ac..3b45db19919 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -281,9 +281,13 @@ const struct IDTypeInfo *BKE_idtype_get_info_from_id(const struct ID *id); const char *BKE_idtype_idcode_to_name(const short idcode); const char *BKE_idtype_idcode_to_name_plural(const short idcode); const char *BKE_idtype_idcode_to_translation_context(const short idcode); -bool BKE_idtype_idcode_is_linkable(const short idcode); + bool BKE_idtype_idcode_is_valid(const short idcode); +bool BKE_idtype_idcode_is_linkable(const short idcode); +/* Macro currently, since any linkable IDtype should be localizable. */ +#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable + short BKE_idtype_idcode_from_name(const char *idtype_name); uint64_t BKE_idtype_idcode_to_idfilter(const short idcode); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 08d78552710..b9e4f2ef44a 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1031,7 +1031,7 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_DISABLED); } else { - const bool disabled = (!BKE_lib_id_make_local(CTX_data_main(C), id, true /* test */, 0) || + const bool disabled = (!BKE_idtype_idcode_is_localizable(GS(id->name)) || (idfrom && idfrom->lib)); but = uiDefIconBut(block, UI_BTYPE_BUT, -- cgit v1.2.3 From 236a9f081462d249043e2bd00a015a6c5cafe377 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 11:45:06 +0200 Subject: IDManagement: refactor: Remove 'test' part from `BKE_lib_id_make_local`. Mixing testing and actual action in a single function is just not a good way to do things, and the 'testing' feature is not used anywhere anymore, time to get rid of it. --- source/blender/blenkernel/BKE_lib_id.h | 2 +- source/blender/blenkernel/intern/brush.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 38 +++++++++------------- .../editors/interface/interface_templates.c | 2 +- .../editors/space_outliner/outliner_tools.c | 2 +- source/blender/makesrna/intern/rna_ID.c | 3 +- .../blender/windowmanager/intern/wm_files_link.c | 3 +- 7 files changed, 22 insertions(+), 30 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7fa21cc0656..f46f946b284 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -248,7 +248,7 @@ enum { }; void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, const int flags); -bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const bool test, const int flags); +bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const int flags); bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 50264f348e9..22d4d00c1c5 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -161,7 +161,7 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ - BKE_lib_id_make_local(bmain, &brush->clone.image->id, false, 0); + BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); } if (!force_local && !force_copy) { diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 6cd42b17e11..89e0ae9e7f0 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -480,10 +480,9 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) * * \param flags: Special flag used when making a whole library's content local, * it needs specific handling. - * - * \return true if the block can be made local. + * \return true is the ID has successfully been made local. */ -bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags) +bool BKE_lib_id_make_local(Main *bmain, ID *id, const int flags) { const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0; @@ -495,23 +494,21 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id); - if (idtype_info != NULL) { - if ((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) { - if (!test) { - if (idtype_info->make_local != NULL) { - idtype_info->make_local(bmain, id, flags); - } - else { - BKE_lib_id_make_local_generic(bmain, id, flags); - } - } - return true; - } + if (idtype_info == NULL) { + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); return false; } - BLI_assert_msg(0, "IDType Missing IDTypeInfo"); - return false; + BLI_assert((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + + if (idtype_info->make_local != NULL) { + idtype_info->make_local(bmain, id, flags); + } + else { + BKE_lib_id_make_local_generic(bmain, id, flags); + } + + return true; } struct IDCopyLibManagementData { @@ -2034,11 +2031,8 @@ void BKE_library_make_local(Main *bmain, * Note that for objects, we don't want proxy pointers to be cleared yet. This will happen * down the road in this function. */ - BKE_lib_id_make_local(bmain, - id, - false, - LIB_ID_MAKELOCAL_FULL_LIBRARY | - LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local( + bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); if (id->newid) { if (GS(id->newid->name) == ID_OB) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index b9e4f2ef44a..9787b101572 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -673,7 +673,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } } else { - if (BKE_lib_id_make_local(bmain, id, false, 0)) { + if (BKE_lib_id_make_local(bmain, id, 0)) { BKE_main_id_newptr_and_tag_clear(bmain); /* Reassign to get proper updates/notifiers. */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 7709c6bb053..062d98c93af 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -739,7 +739,7 @@ static void id_local_fn(bContext *C, Main *bmain = CTX_data_main(C); /* if the ID type has no special local function, * just clear the lib */ - if (BKE_lib_id_make_local(bmain, tselem->id, false, 0) == false) { + if (!BKE_lib_id_make_local(bmain, tselem->id, 0)) { BKE_lib_id_clear_library_data(bmain, tselem->id); } else { diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8f8ad077935..9d8cea851e9 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -931,8 +931,7 @@ static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id) static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool clear_proxy) { - BKE_lib_id_make_local( - bmain, self, false, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); + BKE_lib_id_make_local(bmain, self, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); ID *ret_id = self->newid ? self->newid : self; BKE_id_clear_newpoin(self); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 7568c9989a8..128319b8afe 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -691,7 +691,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, switch (item->append_action) { case WM_APPEND_ACT_COPY_LOCAL: { BKE_lib_id_make_local( - bmain, id, false, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); + bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); if (id->newid != NULL) { if (GS(id->newid->name) == ID_OB) { BKE_rigidbody_ensure_local_object(bmain, (Object *)id->newid); @@ -708,7 +708,6 @@ static void wm_append_do(WMLinkAppendData *lapp_data, case WM_APPEND_ACT_MAKE_LOCAL: BKE_lib_id_make_local(bmain, id, - false, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); BLI_assert(id->newid == NULL); -- cgit v1.2.3 From 1fce66190a46ac47cac015b50913d0b05b3f087e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:14:21 +0200 Subject: Fix/refactor `BKE_id_clear_newpoin` and `BKE_main_id_newptr_and_tag_clear`. Those were not clearing embdeed IDs flags and `newid` pointers at all... --- source/blender/blenkernel/intern/lib_id.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 89e0ae9e7f0..9db2df06e42 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -338,10 +338,29 @@ void id_fake_user_clear(ID *id) void BKE_id_clear_newpoin(ID *id) { - if (id->newid) { - id->newid->tag &= ~LIB_TAG_NEW; + /* We assume that if this ID has no new ID, its embedded data has not either. */ + if (id->newid == NULL) { + return; } + + id->newid->tag &= ~LIB_TAG_NEW; id->newid = NULL; + + /* Deal with embedded data too. */ + Key *key = BKE_key_from_id(id); + if (key != NULL) { + BKE_id_clear_newpoin(&key->id); + } + bNodeTree *ntree = ntreeFromID(id); + if (ntree != NULL) { + BKE_id_clear_newpoin(&ntree->id); + } + if (GS(id->name) == ID_SCE) { + Collection *master_collection = ((Scene *)id)->master_collection; + if (master_collection != NULL) { + BKE_id_clear_newpoin(&master_collection->id); + } + } } static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) @@ -1763,8 +1782,7 @@ void BKE_main_id_newptr_and_tag_clear(Main *bmain) ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - id->newid = NULL; - id->tag &= ~LIB_TAG_NEW; + BKE_id_clear_newpoin(id); } FOREACH_MAIN_ID_END; } -- cgit v1.2.3 From 1d5cc52ef6c1e90ba0ddbd7f0440f20b96b2f321 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:16:35 +0200 Subject: Cleanup: Rename `BKE_id_clear_newpoin` to `BKE_id_newptr_and_tag_clear`. --- source/blender/blenkernel/BKE_lib_id.h | 2 +- source/blender/blenkernel/intern/lib_id.c | 10 +++++----- source/blender/makesrna/intern/rna_ID.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index f46f946b284..36f57209e33 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -230,7 +230,7 @@ void id_us_plus(struct ID *id); void id_us_min(struct ID *id); void id_fake_user_set(struct ID *id); void id_fake_user_clear(struct ID *id); -void BKE_id_clear_newpoin(struct ID *id); +void BKE_id_newptr_and_tag_clear(struct ID *id); /** Flags to control make local code behavior. */ enum { diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 9db2df06e42..daafa587feb 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -336,7 +336,7 @@ void id_fake_user_clear(ID *id) } } -void BKE_id_clear_newpoin(ID *id) +void BKE_id_newptr_and_tag_clear(ID *id) { /* We assume that if this ID has no new ID, its embedded data has not either. */ if (id->newid == NULL) { @@ -349,16 +349,16 @@ void BKE_id_clear_newpoin(ID *id) /* Deal with embedded data too. */ Key *key = BKE_key_from_id(id); if (key != NULL) { - BKE_id_clear_newpoin(&key->id); + BKE_id_newptr_and_tag_clear(&key->id); } bNodeTree *ntree = ntreeFromID(id); if (ntree != NULL) { - BKE_id_clear_newpoin(&ntree->id); + BKE_id_newptr_and_tag_clear(&ntree->id); } if (GS(id->name) == ID_SCE) { Collection *master_collection = ((Scene *)id)->master_collection; if (master_collection != NULL) { - BKE_id_clear_newpoin(&master_collection->id); + BKE_id_newptr_and_tag_clear(&master_collection->id); } } } @@ -1782,7 +1782,7 @@ void BKE_main_id_newptr_and_tag_clear(Main *bmain) ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { - BKE_id_clear_newpoin(id); + BKE_id_newptr_and_tag_clear(id); } FOREACH_MAIN_ID_END; } diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 9d8cea851e9..eb887e1881b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -934,7 +934,7 @@ static struct ID *rna_ID_make_local(struct ID *self, Main *bmain, bool clear_pro BKE_lib_id_make_local(bmain, self, clear_proxy ? 0 : LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); ID *ret_id = self->newid ? self->newid : self; - BKE_id_clear_newpoin(self); + BKE_id_newptr_and_tag_clear(self); return ret_id; } -- cgit v1.2.3 From 4ec2bdbbda05f7460913b65b5b99621021f7f480 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:37:19 +0200 Subject: Cleanup: Add comment about 'make_local' callback of Brush doing bad thing. Callbacks in IDTypeInfo should never affect other IDs if they are not embedded. We break this rule in some cases, at least each of those should be clearly commented about and get some security checks to try to avoid unexpected issues as much as possible. --- source/blender/blenkernel/intern/brush.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 22d4d00c1c5..d70b941695e 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -161,7 +161,13 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) if (brush->clone.image) { /* Special case: ima always local immediately. Clone image should only have one user anyway. */ + /* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided + * in IDType callbacks. Higher-level ID management code usually does not expect such things and + * does not deal properly with it. */ + /* NOTE: assert below ensures that the comment above is valid, and that that exception is + * acceptable for the time being. */ BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); + BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); } if (!force_local && !force_copy) { -- cgit v1.2.3 From 27b6636c4525bd051723d30a148c6a037ea169d8 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:39:40 +0200 Subject: Cleanup: Comment about shpaekey being treated as embedded IDs in `BKE_id_newptr_and_tag_clear`. --- source/blender/blenkernel/intern/lib_id.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index daafa587feb..60b6d7ad66d 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -347,6 +347,9 @@ void BKE_id_newptr_and_tag_clear(ID *id) id->newid = NULL; /* Deal with embedded data too. */ + /* NOTE: even though ShapeKeys are not technically embedded data currently, they behave as such + * in most cases, so for sake of consistency treat them as such here. Also mirrors the behavior + * in `BKE_lib_id_make_local`. */ Key *key = BKE_key_from_id(id); if (key != NULL) { BKE_id_newptr_and_tag_clear(&key->id); -- cgit v1.2.3 From e04a10adabea33cf4b3f92e721f86fdbd50c8ac5 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:40:41 +0200 Subject: Cleanup: no need to clear new flags and pointers from whole Main when making a single ID local. --- source/blender/editors/interface/interface_templates.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 9787b101572..0c9eb20af19 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -674,7 +674,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } else { if (BKE_lib_id_make_local(bmain, id, 0)) { - BKE_main_id_newptr_and_tag_clear(bmain); + BKE_id_newptr_and_tag_clear(id); /* Reassign to get proper updates/notifiers. */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); -- cgit v1.2.3 From 3deb56424f2927ffe8a7ca8ef3de3fa5bc56e304 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 12:41:21 +0200 Subject: Cleanup/Fix outliner 'make local' code. While likely harmless, this code was doing extremely bad thing, by-passing the whole lower-level `BKE_lib_id_make_local` call in case it would fail and deciding by itself to forcefully make the given ID local. Bad. Very, very, very bad. --- source/blender/editors/space_outliner/outliner_tools.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 062d98c93af..9e314701719 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -737,13 +737,8 @@ static void id_local_fn(bContext *C, { if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) { Main *bmain = CTX_data_main(C); - /* if the ID type has no special local function, - * just clear the lib */ - if (!BKE_lib_id_make_local(bmain, tselem->id, 0)) { - BKE_lib_id_clear_library_data(bmain, tselem->id); - } - else { - BKE_main_id_newptr_and_tag_clear(bmain); + if (BKE_lib_id_make_local(bmain, tselem->id, 0)) { + BKE_id_newptr_and_tag_clear(tselem->id); } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { -- cgit v1.2.3 From e97653ccf3356c57100606f0a0a175dbf4676df7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 14:26:49 +0200 Subject: Fix: properly implement the 'only append' execption case for WorkSpaces. Add needed extra flag and utils to IDType to mark and check an ID type as only appendable. Note that this is only a loose user-level enforcement currently, in any case you need to be able to link an ID to append it currently, so for low-level code this does not really matter. Currently only WorkSpace and Screen ID types are marked with the new `IDTYPE_FLAGS_ONLY_APPEND` flag. --- source/blender/blenkernel/BKE_idtype.h | 5 +++++ source/blender/blenkernel/intern/idtype.c | 24 +++++++++++++++++++--- source/blender/blenkernel/intern/screen.c | 2 +- source/blender/blenkernel/intern/workspace.c | 2 +- .../blender/windowmanager/intern/wm_files_link.c | 5 ++--- 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 3b45db19919..7136a3fd7af 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -45,6 +45,10 @@ enum { IDTYPE_FLAGS_NO_COPY = 1 << 0, /** Indicates that the given IDType does not support linking/appending from a library file. */ IDTYPE_FLAGS_NO_LIBLINKING = 1 << 1, + /** Indicates that the given IDType should not be directly linked from a library file, but may be + * appended. + * NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */ + IDTYPE_FLAGS_ONLY_APPEND = 1 << 2, /** Indicates that the given IDType does not have animation data. */ IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3, }; @@ -285,6 +289,7 @@ const char *BKE_idtype_idcode_to_translation_context(const short idcode); bool BKE_idtype_idcode_is_valid(const short idcode); bool BKE_idtype_idcode_is_linkable(const short idcode); +bool BKE_idtype_idcode_is_only_appendable(const short idcode); /* Macro currently, since any linkable IDtype should be localizable. */ #define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index fee70922570..b2efccc53c4 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -224,10 +224,10 @@ bool BKE_idtype_idcode_is_valid(const short idcode) } /** - * Return non-zero when an ID type is linkable. + * Check if an ID type is linkable. * - * \param idcode: The code to check. - * \return Boolean, 0 when non linkable. + * \param idcode: The IDType code to check. + * \return Boolean, false when non linkable, true otherwise. */ bool BKE_idtype_idcode_is_linkable(const short idcode) { @@ -236,6 +236,24 @@ bool BKE_idtype_idcode_is_linkable(const short idcode) return id_type != NULL ? (id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 : false; } +/** + * Check if an ID type is only appendable. + * + * \param idcode: The IDType code to check. + * \return Boolean, false when also linkable, true when only appendable. + */ +bool BKE_idtype_idcode_is_only_appendable(const short idcode) +{ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode); + BLI_assert(id_type != NULL); + if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_ONLY_APPEND) != 0) { + /* Only appendable ID types should also always be linkable. */ + BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + return true; + } + return false; +} + /** * Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB). */ diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 60da008a282..73e25a22225 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -312,7 +312,7 @@ IDTypeInfo IDType_ID_SCR = { .name = "Screen", .name_plural = "screens", .translation_context = BLT_I18NCONTEXT_ID_SCREEN, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = NULL, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index d7368e62437..3c168a6c7b2 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_WS = { .name = "WorkSpace", .name_plural = "workspaces", .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE, - .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA, .init_data = workspace_init_data, .copy_data = NULL, diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 128319b8afe..adb1596c4e1 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -944,9 +944,8 @@ static bool wm_link_append_item_poll(ReportList *reports, idcode = BKE_idtype_idcode_from_name(group); - /* XXX For now, we do a nasty exception for workspace, forbid linking them. - * Not nice, ultimately should be solved! */ - if (!BKE_idtype_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) { + if (!BKE_idtype_idcode_is_linkable(idcode) || + (!do_append && BKE_idtype_idcode_is_only_appendable(idcode))) { if (reports) { if (do_append) { BKE_reportf(reports, -- cgit v1.2.3 From 08aa0eb15da10d5368623e183b8de4fb059d48fd Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 16 Sep 2021 15:31:03 +0200 Subject: Cleanup: WM append code: de-duplicate post-make-local process a bit. --- .../blender/windowmanager/intern/wm_files_link.c | 37 ++++++++++------------ 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index adb1596c4e1..29e34313be5 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -688,21 +688,12 @@ static void wm_append_do(WMLinkAppendData *lapp_data, continue; } + ID *local_appended_new_id = NULL; switch (item->append_action) { case WM_APPEND_ACT_COPY_LOCAL: { BKE_lib_id_make_local( bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); - if (id->newid != NULL) { - if (GS(id->newid->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)id->newid); - } - if (set_fakeuser) { - if (!ELEM(GS(id->name), ID_OB, ID_GR)) { - /* Do not set fake user on objects nor collections (instancing). */ - id_fake_user_set(id->newid); - } - } - } + local_appended_new_id = id->newid; break; } case WM_APPEND_ACT_MAKE_LOCAL: @@ -711,15 +702,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); BLI_assert(id->newid == NULL); - if (GS(id->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)id); - } - if (set_fakeuser) { - if (!ELEM(GS(id->name), ID_OB, ID_GR)) { - /* Do not set fake user on objects nor collections (instancing). */ - id_fake_user_set(id); - } - } + local_appended_new_id = id; break; case WM_APPEND_ACT_KEEP_LINKED: /* Nothing to do here. */ @@ -727,7 +710,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, case WM_APPEND_ACT_REUSE_LOCAL: /* We only need to set `newid` to ID found in previous loop, for proper remapping. */ ID_NEW_SET(id->newid, item->customdata); - /* Do not set again fake user in case we reuse existing local ID. */ + /* This is not a 'new' local appended id, do not set `local_appended_new_id` here. */ break; case WM_APPEND_ACT_UNSET: CLOG_ERROR( @@ -736,6 +719,18 @@ static void wm_append_do(WMLinkAppendData *lapp_data, default: BLI_assert(0); } + + if (local_appended_new_id != NULL) { + if (GS(local_appended_new_id->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id); + } + if (set_fakeuser) { + if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) { + /* Do not set fake user on objects nor collections (instancing). */ + id_fake_user_set(local_appended_new_id); + } + } + } } /* Remap IDs as needed. */ -- cgit v1.2.3 From c938d9a0e554b49354e7f6bb8168b88c279cb2c0 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 15 Sep 2021 13:06:23 +0200 Subject: Cleanup: Rename USER_APP_LOCK_UI_LAYOUT There will be other settings that lock other aspects of the UI layout (e.g., resizing of editors). So better to name this setting what it actually handles (the corners). New name: USER_APP_LOCK_CORNER_SPLIT Differential Revision: D12516 --- source/blender/editors/screen/area.c | 2 +- source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/makesrna/intern/rna_userdef.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e08a4e946f6..e907c7e153f 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -882,7 +882,7 @@ static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area) return; } - if (U.app_flag & USER_APP_LOCK_UI_LAYOUT) { + if (U.app_flag & USER_APP_LOCK_CORNER_SPLIT) { return; } diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 7160d2c3751..37a3d60a647 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1135,7 +1135,7 @@ typedef enum eUserpref_TableAPI { /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { - USER_APP_LOCK_UI_LAYOUT = (1 << 0), + USER_APP_LOCK_CORNER_SPLIT = (1 << 0), } eUserpref_APP_Flag; /** #UserDef.statusbar_flag */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 13aea804957..e61570b43e5 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4591,7 +4591,7 @@ static void rna_def_userdef_view(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update"); prop = RNA_def_property(srna, "show_layout_ui", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_UI_LAYOUT); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_CORNER_SPLIT); RNA_def_property_ui_text( prop, "Editor Corner Splitting", "Split and join editors by dragging from corners"); RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); -- cgit v1.2.3 From 9fee59a4849cdedf0ce5332ec78c70dd128aa3b1 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 15 Sep 2021 13:07:52 +0200 Subject: Rename show_layout_ui > show_corner_split and remove from UI This breaks API compatibility. However we are now grouping this setting in the proper section (preferences.apps), so scripts had to update anyways. So they may as well do it for the final name. The reason to remove from the UI is that this is intended for app setup, and as such it should not be exposed to final users until we have apps better presented (for 3.1 hopefully). Differential Revision: D12516 --- release/scripts/startup/bl_ui/space_userpref.py | 1 - source/blender/makesrna/intern/rna_userdef.c | 36 ++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 1711519bce1..0093110d326 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -267,7 +267,6 @@ class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel): col = layout.column() col.prop(system, "use_region_overlap") - col.prop(view, "show_layout_ui", text="Corner Splitting") col.prop(view, "show_navigate_ui") col.prop(view, "color_picker_type") col.row().prop(view, "header_align") diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index e61570b43e5..0a123c59ee2 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -559,6 +559,11 @@ static PointerRNA rna_UserDef_system_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_PreferencesSystem, ptr->data); } +static PointerRNA rna_UserDef_apps_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_PreferencesApps, ptr->data); +} + static void rna_UserDef_audio_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) { BKE_sound_init(bmain); @@ -4590,12 +4595,6 @@ static void rna_def_userdef_view(BlenderRNA *brna) "Color range used for weight visualization in weight painting mode"); RNA_def_property_update(prop, 0, "rna_UserDef_weight_color_update"); - prop = RNA_def_property(srna, "show_layout_ui", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_CORNER_SPLIT); - RNA_def_property_ui_text( - prop, "Editor Corner Splitting", "Split and join editors by dragging from corners"); - RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); - prop = RNA_def_property(srna, "show_navigate_ui", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_SHOW_GIZMO_NAVIGATE); RNA_def_property_ui_text( @@ -6230,6 +6229,24 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Asset Libraries", ""); } +static void rna_def_userdef_apps(BlenderRNA *brna) +{ + PropertyRNA *prop; + StructRNA *srna; + + srna = RNA_def_struct(brna, "PreferencesApps", NULL); + RNA_def_struct_sdna(srna, "UserDef"); + RNA_def_struct_nested(brna, srna, "Preferences"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Apps", "Preferences that work only for apps"); + + prop = RNA_def_property(srna, "show_corner_split", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_CORNER_SPLIT); + RNA_def_property_ui_text( + prop, "Corner Splitting", "Split and join editors by dragging from corners"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); +} + static void rna_def_userdef_experimental(BlenderRNA *brna) { StructRNA *srna; @@ -6445,6 +6462,12 @@ void RNA_def_userdef(BlenderRNA *brna) RNA_def_property_ui_text( prop, "System & OpenGL", "Graphics driver and operating system settings"); + prop = RNA_def_property(srna, "apps", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "PreferencesApps"); + RNA_def_property_pointer_funcs(prop, "rna_UserDef_apps_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Apps", "Preferences that work only for apps"); + prop = RNA_def_property(srna, "experimental", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "PreferencesExperimental"); @@ -6506,6 +6529,7 @@ void RNA_def_userdef(BlenderRNA *brna) rna_def_userdef_studiolights(brna); rna_def_userdef_studiolight(brna); rna_def_userdef_pathcompare(brna); + rna_def_userdef_apps(brna); rna_def_userdef_experimental(brna); USERDEF_TAG_DIRTY_PROPERTY_UPDATE_DISABLE; -- cgit v1.2.3 From f2cfad77f9f8876edfdc9930206fc12db71f25b0 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 16 Sep 2021 15:27:36 +0200 Subject: App Settings: Regions Visbility Toggle The toggle that allow users to "show" the region (header, toolbar, ...) when it is collapsed can now be configured for the apps. Note: This option is not visibile in the UI. Differential Revision: D12516 --- source/blender/editors/screen/area.c | 4 ++++ source/blender/makesdna/DNA_userdef_types.h | 1 + source/blender/makesrna/intern/rna_userdef.c | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index e907c7e153f..384445db9fb 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1058,6 +1058,10 @@ static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscre return false; } + if (is_hidden && (U.app_flag & USER_APP_HIDE_REGION_TOGGLE)) { + return false; + } + return true; } diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 37a3d60a647..2203068928c 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1136,6 +1136,7 @@ typedef enum eUserpref_TableAPI { /** #UserDef.app_flag */ typedef enum eUserpref_APP_Flag { USER_APP_LOCK_CORNER_SPLIT = (1 << 0), + USER_APP_HIDE_REGION_TOGGLE = (1 << 1), } eUserpref_APP_Flag; /** #UserDef.statusbar_flag */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 0a123c59ee2..a457cb980ee 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6245,6 +6245,12 @@ static void rna_def_userdef_apps(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Corner Splitting", "Split and join editors by dragging from corners"); RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); + + prop = RNA_def_property(srna, "show_regions_visibility_toggle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_HIDE_REGION_TOGGLE); + RNA_def_property_ui_text( + prop, "Regions Visibility Toggle", "Header and side bars visibility toggles"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); } static void rna_def_userdef_experimental(BlenderRNA *brna) -- cgit v1.2.3 From 867e6ffe88f830cbfa158de95dea7cbb155e3fdb Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 15 Sep 2021 13:12:40 +0200 Subject: App Settings: Edge Resize This prevents both editor resize as well as regions (e.g., the toolbar). Note: This option is not visible in the UI. Differential Revision: D12516 --- source/blender/editors/screen/area.c | 4 ++++ source/blender/editors/screen/screen_geometry.c | 4 ++++ source/blender/makesdna/DNA_userdef_types.h | 1 + source/blender/makesrna/intern/rna_userdef.c | 5 +++++ 4 files changed, 14 insertions(+) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 384445db9fb..9546035375c 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1062,6 +1062,10 @@ static bool region_azone_edge_poll(const ARegion *region, const bool is_fullscre return false; } + if (!is_hidden && (U.app_flag & USER_APP_LOCK_EDGE_RESIZE)) { + return false; + } + return true; } diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index 51edad0332b..e67c933cb8e 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -130,6 +130,10 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win, const int mx, const int my) { + if (U.app_flag & USER_APP_LOCK_EDGE_RESIZE) { + return NULL; + } + /* Use layout size (screen excluding global areas) for screen-layout area edges */ rcti screen_rect; WM_window_screen_rect_calc(win, &screen_rect); diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 2203068928c..5137fc30f95 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1137,6 +1137,7 @@ typedef enum eUserpref_TableAPI { typedef enum eUserpref_APP_Flag { USER_APP_LOCK_CORNER_SPLIT = (1 << 0), USER_APP_HIDE_REGION_TOGGLE = (1 << 1), + USER_APP_LOCK_EDGE_RESIZE = (1 << 2), } eUserpref_APP_Flag; /** #UserDef.statusbar_flag */ diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index a457cb980ee..ff33dc8a2ee 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6246,6 +6246,11 @@ static void rna_def_userdef_apps(BlenderRNA *brna) prop, "Corner Splitting", "Split and join editors by dragging from corners"); RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); + prop = RNA_def_property(srna, "show_edge_resize", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_LOCK_EDGE_RESIZE); + RNA_def_property_ui_text(prop, "Edge Resize", "Resize editors by dragging from the edges"); + RNA_def_property_update(prop, 0, "rna_userdef_screen_update"); + prop = RNA_def_property(srna, "show_regions_visibility_toggle", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "app_flag", USER_APP_HIDE_REGION_TOGGLE); RNA_def_property_ui_text( -- cgit v1.2.3 From 73ed07648901c047887f2e8e94750a3eae9afb08 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Wed, 15 Sep 2021 17:10:38 +0200 Subject: Hide empty keymap warnings when running a template --- source/blender/windowmanager/intern/wm_keymap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index 25bcf1967ea..f1fe3e89007 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -460,8 +460,11 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) if (UNLIKELY(BLI_listbase_is_empty(&keymap->items))) { /* Empty key-maps may be missing more there may be a typo in the name. - * Warn early to avoid losing time investigating each case. */ - CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); + * Warn early to avoid losing time investigating each case. + * When developing a customized Blender though you may want empty keymaps. */ + if (!U.app_template[0]) { + CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); + } } if (keymap->poll != NULL) { -- cgit v1.2.3 From c9daab7936562b1e02262cd28f1d6cc4dde91d71 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 16 Sep 2021 16:39:51 +0200 Subject: Assets: Recursive reading of asset libraries With this, asset libraries can be directory structures and all assets in sub-directories will show up in an Asset Browser. With complex directory structures and many .blend files inside, asset library reading will be quite slow for now. There's initial work being done to introduce indexing for this (T82979), other optimizations are being discussed as well. Addresses T91406. Differential Revision: https://developer.blender.org/D12139 --- source/blender/blenloader/intern/versioning_300.c | 13 +++++++++++++ source/blender/editors/space_file/filesel.c | 3 ++- source/blender/makesdna/DNA_space_types.h | 7 +++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 55aed4ddc2b..538634f4c9e 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1261,5 +1261,18 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)sl; + if (sfile->asset_params) { + sfile->asset_params->base_params.recursion_level = FILE_SELECT_MAX_RECURSIONS; + } + } + } + } + } } } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 4ab7014cf82..11b06d2b414 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -135,7 +135,8 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; base_params->display = FILE_IMGDISPLAY; base_params->sort = FILE_SORT_ALPHA; - base_params->recursion_level = 1; + /* Asset libraries include all sub-directories, so enable maximal recursion. */ + base_params->recursion_level = FILE_SELECT_MAX_RECURSIONS; /* 'SMALL' size by default. More reasonable since this is typically used as regular editor, * space is more of an issue here. */ base_params->thumbnail_size = 96; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 13e6904662e..5475e1bacd8 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -913,6 +913,13 @@ enum eFileDetails { #define FILE_MAX_LIBEXTRA (FILE_MAX + MAX_ID_NAME) +/** + * Maximum level of recursions accepted for #FileSelectParams.recursion_level. Rather than a + * completely arbitrary limit or none at all, make it just enough to support the most extreme case + * where the maximal path length is used with single letter directory/file names only. + */ +#define FILE_SELECT_MAX_RECURSIONS (FILE_MAX_LIBEXTRA / 2) + /* filesel types */ typedef enum eFileSelectType { FILE_LOADLIB = 1, -- cgit v1.2.3 From 58043c0637f88f56a26c9cd645d9019780bc4761 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 16 Sep 2021 18:38:27 +0200 Subject: Rename App Menu to Blender Menu "App" is a name reserved for the application templates at the moment. It may become its own term in the near future if Templates are separated from "Apps". So since this is a name not exposed to the users we should renamed it sooner than later. Note that this will require scripts to update since the name of the class is renamed here. This also requires an update in the User Manual. Differential Revision: https://developer.blender.org/D12523 --- release/scripts/startup/bl_ui/space_topbar.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index bacca6dedc2..b409e9079be 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -209,9 +209,9 @@ class TOPBAR_MT_editor_menus(Menu): # Allow calling this menu directly (this might not be a header area). if getattr(context.area, "show_menus", False): - layout.menu("TOPBAR_MT_app", text="", icon='BLENDER') + layout.menu("TOPBAR_MT_blender", text="", icon='BLENDER') else: - layout.menu("TOPBAR_MT_app", text="Blender") + layout.menu("TOPBAR_MT_blender", text="Blender") layout.menu("TOPBAR_MT_file") layout.menu("TOPBAR_MT_edit") @@ -222,7 +222,7 @@ class TOPBAR_MT_editor_menus(Menu): layout.menu("TOPBAR_MT_help") -class TOPBAR_MT_app(Menu): +class TOPBAR_MT_blender(Menu): bl_label = "Blender" def draw(self, _context): @@ -238,7 +238,7 @@ class TOPBAR_MT_app(Menu): layout.separator() - layout.menu("TOPBAR_MT_app_system") + layout.menu("TOPBAR_MT_blender_system") class TOPBAR_MT_file_cleanup(Menu): @@ -430,7 +430,7 @@ class TOPBAR_MT_file_defaults(Menu): # Include technical operators here which would otherwise have no way for users to access. -class TOPBAR_MT_app_system(Menu): +class TOPBAR_MT_blender_system(Menu): bl_label = "System" def draw(self, _context): @@ -854,8 +854,8 @@ classes = ( TOPBAR_MT_file_context_menu, TOPBAR_MT_workspace_menu, TOPBAR_MT_editor_menus, - TOPBAR_MT_app, - TOPBAR_MT_app_system, + TOPBAR_MT_blender, + TOPBAR_MT_blender_system, TOPBAR_MT_file, TOPBAR_MT_file_new, TOPBAR_MT_file_recover, -- cgit v1.2.3 From 31a0708cb1097be96bf7a8ad2b344b20ac7d2fcd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 16 Sep 2021 11:56:31 -0500 Subject: Cleanup: Add built-in check for an attribute ID --- source/blender/blenkernel/BKE_geometry_set.hh | 1 + source/blender/blenkernel/intern/attribute_access.cc | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3da35cb4fe1..bf38294257a 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -103,6 +103,7 @@ class GeometryComponent { virtual int attribute_domain_size(const AttributeDomain domain) const; bool attribute_is_builtin(const blender::StringRef attribute_name) const; + bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const; /* Get read-only access to the highest priority attribute with the given name. * Returns null if the attribute does not exist. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index bdf1891a55a..cfd3136c765 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -817,6 +817,12 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_ return providers->builtin_attribute_providers().contains_as(attribute_name); } +bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const +{ + /* Anonymous attributes cannot be built-in. */ + return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name()); +} + blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read( const AttributeIDRef &attribute_id) const { @@ -1210,7 +1216,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, BLI_assert(cpp_type != nullptr); const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions(); - if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) { + if (component.attribute_is_builtin(attribute_id)) { const StringRef attribute_name = attribute_id.name(); WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name); if (!attribute) { -- cgit v1.2.3 From ddb7cb7e4ab85d323fe23e4879196f4b1a23d4f5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 16 Sep 2021 12:03:32 -0500 Subject: Geometry Nodes: Simplify using OutputAttribute in a vector Store the optional temporary span storage as a unique_ptr and move it in the move constructor, to avoid the need to add a special move constructor that clears the "show_warning" fields from it. Maybe this is very slightly slower, but we'll need this class less often in the future anyway. --- source/blender/blenkernel/BKE_attribute_access.hh | 17 +++++++++++++---- source/blender/blenkernel/intern/attribute_access.cc | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 9d309d8a1c1..7a4dc5270af 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -247,7 +247,7 @@ class OutputAttribute { GVMutableArrayPtr varray_; AttributeDomain domain_; SaveFn save_; - std::optional optional_span_varray_; + std::unique_ptr optional_span_varray_; bool ignore_old_values_ = false; bool save_has_been_called_ = false; @@ -265,7 +265,15 @@ class OutputAttribute { { } - OutputAttribute(OutputAttribute &&other) = default; + OutputAttribute(OutputAttribute &&other) + : varray_(std::move(other.varray_)), + domain_(other.domain_), + save_(other.save_), + optional_span_varray_(std::move(other.optional_span_varray_)), + ignore_old_values_(other.ignore_old_values_), + save_has_been_called_(other.save_has_been_called_) + { + } ~OutputAttribute(); @@ -306,9 +314,10 @@ class OutputAttribute { fn::GMutableSpan as_span() { - if (!optional_span_varray_.has_value()) { + if (!optional_span_varray_) { const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_.emplace(*varray_, materialize_old_values); + optional_span_varray_ = std::make_unique(*varray_, + materialize_old_values); } fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; return span_varray; diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index cfd3136c765..ee0477faefe 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -190,7 +190,7 @@ AttributeDomain attribute_domain_highest_priority(Span domains) void OutputAttribute::save() { save_has_been_called_ = true; - if (optional_span_varray_.has_value()) { + if (optional_span_varray_) { optional_span_varray_->save(); } if (save_) { -- cgit v1.2.3 From f81bacd6f0fc8d9afb46570e1a66c4469185d8a5 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 16 Sep 2021 12:25:26 -0500 Subject: Geometry Nodes: Transfer attributes in the curve to mesh node This patch allows point and spline attributes to be transferred to the mesh generated by the curve to mesh node. All dynamic named and anonymous attributes are transferred. So a user-created attribute will be transferred, but "radius", "tilt" or the handle position attributes won't be transferred by default and will need to be copied first. This trade-off is made for performance, since most of the time, users won't need these attributes copied. Generally, attributes are transferred to the point/vertex domain. However, if they have the same name as a built-in mesh attribute that only exists on a different domain, like "shade_smooth", then they can be transferred directly to that domain as well. Conversion directly to the face corner domain is not necessary because there are no builtin face corner attributes. I see this conversion directly to other domains as an optimization we could use behind the scenes in the future as well, when named attributes are less common. For performance, I haven't tested which of the following is better: ``` for each spline combination: for each attribute: for each attribute: for each spline combination: ``` For now I used the existing loop to avoid more threading overhead. Differential Revision: https://developer.blender.org/D12363 --- .../nodes/geometry/nodes/node_geo_curve_to_mesh.cc | 509 ++++++++++++++++++--- 1 file changed, 452 insertions(+), 57 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index 2eae11d1705..f46440fd949 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -39,6 +39,20 @@ static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) b.add_output("Mesh"); } +/** Information about the creation of one curve spline and profile spline combination. */ +struct ResultInfo { + const Spline &spline; + const Spline &profile; + int vert_offset; + int edge_offset; + int loop_offset; + int poly_offset; + int spline_vert_len; + int spline_edge_len; + int profile_vert_len; + int profile_edge_len; +}; + static void vert_extrude_to_mesh_data(const Spline &spline, const float3 profile_vert, MutableSpan r_verts, @@ -75,44 +89,33 @@ static void mark_edges_sharp(MutableSpan edges) } } -static void spline_extrude_to_mesh_data(const Spline &spline, - const Spline &profile_spline, - const int vert_offset, - const int edge_offset, - const int loop_offset, - const int poly_offset, +static void spline_extrude_to_mesh_data(const ResultInfo &info, MutableSpan r_verts, MutableSpan r_edges, MutableSpan r_loops, MutableSpan r_polys) { - const int spline_vert_len = spline.evaluated_points_size(); - const int spline_edge_len = spline.evaluated_edges_size(); - const int profile_vert_len = profile_spline.evaluated_points_size(); - const int profile_edge_len = profile_spline.evaluated_edges_size(); - if (spline_vert_len == 0) { - return; - } - - if (profile_vert_len == 1) { + const Spline &spline = info.spline; + const Spline &profile = info.profile; + if (info.profile_vert_len == 1) { vert_extrude_to_mesh_data(spline, - profile_spline.evaluated_positions()[0], + profile.evaluated_positions()[0], r_verts, r_edges, - vert_offset, - edge_offset); + info.vert_offset, + info.edge_offset); return; } /* Add the edges running along the length of the curve, starting at each profile vertex. */ - const int spline_edges_start = edge_offset; - for (const int i_profile : IndexRange(profile_vert_len)) { - const int profile_edge_offset = spline_edges_start + i_profile * spline_edge_len; - for (const int i_ring : IndexRange(spline_edge_len)) { - const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + const int spline_edges_start = info.edge_offset; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; - const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; MEdge &edge = r_edges[profile_edge_offset + i_ring]; edge.v1 = ring_vert_offset + i_profile; @@ -122,13 +125,14 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } /* Add the edges running along each profile ring. */ - const int profile_edges_start = spline_edges_start + profile_vert_len * spline_edge_len; - for (const int i_ring : IndexRange(spline_vert_len)) { - const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int profile_edges_start = spline_edges_start + + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int ring_edge_offset = profile_edges_start + i_ring * profile_edge_len; - for (const int i_profile : IndexRange(profile_edge_len)) { - const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; MEdge &edge = r_edges[ring_edge_offset + i_profile]; edge.v1 = ring_vert_offset + i_profile; @@ -138,24 +142,25 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } /* Calculate poly and corner indices. */ - for (const int i_ring : IndexRange(spline_edge_len)) { - const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; - const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; - const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring; - const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring; + const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; - const int ring_poly_offset = poly_offset + i_ring * profile_edge_len; - const int ring_loop_offset = loop_offset + i_ring * profile_edge_len * 4; + const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; + const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; - for (const int i_profile : IndexRange(profile_edge_len)) { + for (const int i_profile : IndexRange(info.profile_edge_len)) { const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; - const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; - const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile; - const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile; + const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + + info.spline_edge_len * i_next_profile; MPoly &poly = r_polys[ring_poly_offset + i_profile]; poly.loopstart = ring_segment_loop_offset; @@ -181,29 +186,30 @@ static void spline_extrude_to_mesh_data(const Spline &spline, Span positions = spline.evaluated_positions(); Span tangents = spline.evaluated_tangents(); Span normals = spline.evaluated_normals(); - Span profile_positions = profile_spline.evaluated_positions(); + Span profile_positions = profile.evaluated_positions(); GVArray_Typed radii = spline.interpolate_to_evaluated(spline.radii()); - for (const int i_ring : IndexRange(spline_vert_len)) { + for (const int i_ring : IndexRange(info.spline_vert_len)) { float4x4 point_matrix = float4x4::from_normalized_axis_data( positions[i_ring], normals[i_ring], tangents[i_ring]); point_matrix.apply_scale(radii[i_ring]); - const int ring_vert_start = vert_offset + i_ring * profile_vert_len; - for (const int i_profile : IndexRange(profile_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { MVert &vert = r_verts[ring_vert_start + i_profile]; copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); } } /* Mark edge loops from sharp vector control points sharp. */ - if (profile_spline.type() == Spline::Type::Bezier) { - const BezierSpline &bezier_spline = static_cast(profile_spline); + if (profile.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(profile); Span control_point_offsets = bezier_spline.control_point_offsets(); for (const int i : IndexRange(bezier_spline.size())) { if (bezier_spline.point_is_sharp(i)) { - mark_edges_sharp(r_edges.slice( - spline_edges_start + spline_edge_len * control_point_offsets[i], spline_edge_len)); + mark_edges_sharp( + r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], + info.spline_edge_len)); } } } @@ -272,6 +278,372 @@ static ResultOffsets calculate_result_offsets(Span profiles, Span meta_data = component.attribute_get_meta_data(attribute_id); + if (!meta_data) { + /* This function has to return something in this case, but it shouldn't be used, + * so return an output that will assert later if the code attempts to handle it. */ + return ATTR_DOMAIN_AUTO; + } + + return meta_data->domain; +} + +/** + * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling + * `as_span()` for every single profile and curve spline combination, and for readability. + */ +struct ResultAttributeData { + GMutableSpan data; + AttributeDomain domain; +}; + +static std::optional create_attribute_and_get_span( + MeshComponent &component, + const AttributeIDRef &attribute_id, + AttributeMetaData meta_data, + Vector &r_attributes) +{ + const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, meta_data.data_type); + if (!attribute) { + return std::nullopt; + } + + GMutableSpan span = attribute.as_span(); + r_attributes.append(std::move(attribute)); + return std::make_optional({span, domain}); +} + +/** + * Store the references to the attribute data from the curve and profile inputs. Here we rely on + * the invariants of the storage of curve attributes, that the order will be consistent between + * splines, and all splines will have the same attributes. + */ +struct ResultAttributes { + /** + * Result attributes on the mesh corresponding to each attribute on the curve input, in the same + * order. The data is optional only in case the attribute does not exist on the mesh for some + * reason, like "shade_smooth" when the result has no faces. + */ + Vector> curve_point_attributes; + Vector> curve_spline_attributes; + + /** + * Result attributes corresponding the attributes on the profile input, in the same order. The + * attributes are optional in case the attribute names correspond to a namse used by the curve + * input, in which case the curve input attributes take precedence. + */ + Vector> profile_point_attributes; + Vector> profile_spline_attributes; + + /** + * Because some builtin attributes are not stored contiguously, and the curve inputs might have + * attributes with those names, it's necessary to keep OutputAttributes around to give access to + * the result data in a contiguous array. + */ + Vector attributes; +}; +static ResultAttributes create_result_attributes(const CurveEval &curve, + const CurveEval &profile, + Mesh &mesh) +{ + MeshComponent mesh_component; + mesh_component.replace(&mesh, GeometryOwnershipType::Editable); + Set curve_attributes; + + /* In order to prefer attributes on the main curve input when there are name collisions, first + * check the attributes on the curve, then add attributes on the profile that are not also on the + * main curve input. */ + ResultAttributes result; + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_POINT); + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_CURVE); + profile.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_point_attributes.append({}); + } + else { + result.profile_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_POINT); + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_spline_attributes.append({}); + } + else { + result.profile_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_CURVE); + + return result; +} + +template +static void copy_curve_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; + dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; + dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +static void copy_curve_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_curve_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_curve_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_curve_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +template +static void copy_profile_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + dst[profile_vert_start + i_profile] = src[i_profile]; + } + } +} + +template +static void copy_profile_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; + dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); + } +} + +template +static void copy_profile_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + dst[profile_face_start + i_profile] = src[i_profile]; + } + } +} + +static void copy_profile_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_profile_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_profile_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_profile_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, + ResultAttributes &attributes) +{ + if (!attributes.curve_point_attributes.is_empty()) { + int i = 0; + info.spline.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_point_attributes[i]) { + copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), + info, + *attributes.curve_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + if (!attributes.profile_point_attributes.is_empty()) { + int i = 0; + info.profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_point_attributes[i]) { + copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), + info, + *attributes.profile_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } +} + +template +static void copy_spline_data_to_mesh(Span src, Span offsets, MutableSpan dst) +{ + for (const int i : IndexRange(src.size())) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } +} + +/** + * Since the offsets for each combination of curve and profile spline are stored for every mesh + * domain, and this just needs to fill the chunks corresponding to each combination, we can use + * the same function for all mesh domains. + */ +static void copy_spline_attribute_to_mesh(const GSpan src, + const ResultOffsets &offsets, + ResultAttributeData &dst_attribute) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_spline_data_to_mesh(src.typed(), offsets.vert, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_spline_data_to_mesh(src.typed(), offsets.edge, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_spline_data_to_mesh(src.typed(), offsets.poly, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + copy_spline_data_to_mesh(src.typed(), offsets.loop, dst_attribute.data.typed()); + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, + const CurveEval &profile, + const ResultOffsets &offsets, + ResultAttributes &attributes) +{ + if (!attributes.curve_spline_attributes.is_empty()) { + int i = 0; + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), + offsets, + *attributes.curve_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } + if (!attributes.profile_spline_attributes.is_empty()) { + int i = 0; + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), + offsets, + *attributes.profile_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } +} + /** * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. * Although it would be a sensible decision to use the better topology information available while @@ -296,27 +668,50 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr mesh->smoothresh = DEG2RADF(180.0f); BKE_mesh_normals_tag_dirty(mesh); + ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); + threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { for (const int i_spline : curves_range) { + const Spline &spline = *curves[i_spline]; + if (spline.evaluated_points_size() == 0) { + continue; + } const int spline_start_index = i_spline * profiles.size(); threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { for (const int i_profile : profiles_range) { + const Spline &profile = *profiles[i_profile]; const int i_mesh = spline_start_index + i_profile; - spline_extrude_to_mesh_data(*curves[i_spline], - *profiles[i_profile], - offsets.vert[i_mesh], - offsets.edge[i_mesh], - offsets.loop[i_mesh], - offsets.poly[i_mesh], + ResultInfo info{ + spline, + profile, + offsets.vert[i_mesh], + offsets.edge[i_mesh], + offsets.loop[i_mesh], + offsets.poly[i_mesh], + spline.evaluated_points_size(), + spline.evaluated_edges_size(), + profile.evaluated_points_size(), + profile.evaluated_edges_size(), + }; + + spline_extrude_to_mesh_data(info, {mesh->mvert, mesh->totvert}, {mesh->medge, mesh->totedge}, {mesh->mloop, mesh->totloop}, {mesh->mpoly, mesh->totpoly}); + + copy_point_domain_attributes_to_mesh(info, attributes); } }); } }); + copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); + + for (OutputAttribute &output_attribute : attributes.attributes) { + output_attribute.save(); + } + return mesh; } -- cgit v1.2.3 From db7fca3588aab72e49a74cbb2c236f86c0e0e6c1 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 16 Sep 2021 12:32:40 -0500 Subject: Cleanup: Remove unnecessary manual of move constructor Turns out this isn't actually necessary. --- source/blender/blenkernel/BKE_attribute_access.hh | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 7a4dc5270af..cf54e7efa0d 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -265,15 +265,7 @@ class OutputAttribute { { } - OutputAttribute(OutputAttribute &&other) - : varray_(std::move(other.varray_)), - domain_(other.domain_), - save_(other.save_), - optional_span_varray_(std::move(other.optional_span_varray_)), - ignore_old_values_(other.ignore_old_values_), - save_has_been_called_(other.save_has_been_called_) - { - } + OutputAttribute(OutputAttribute &&other) = default; ~OutputAttribute(); -- cgit v1.2.3